diff --git a/.ci/scripts/test.sh b/.ci/scripts/test.sh index d98c85b375..233f70462b 100755 --- a/.ci/scripts/test.sh +++ b/.ci/scripts/test.sh @@ -213,7 +213,7 @@ elif [[ -n "${TAV_MODULE}" ]]; then memcached) DOCKER_COMPOSE_FILE=docker-compose-memcached.yml ;; - aws-sdk|@aws-sdk/client-s3) + aws-sdk|@aws-sdk/client-s3|@aws-sdk/client-dynamodb) DOCKER_COMPOSE_FILE=docker-compose-localstack.yml ;; *) diff --git a/.ci/tav.json b/.ci/tav.json index 2e2d90234c..fea2861db9 100644 --- a/.ci/tav.json +++ b/.ci/tav.json @@ -4,6 +4,7 @@ "modules": [ "@apollo/server", "@aws-sdk/client-s3", + "@aws-sdk/client-dynamodb", "@elastic/elasticsearch", "@hapi/hapi", "@opentelemetry/api", diff --git a/.tav.yml b/.tav.yml index c73b05f952..e46a8f745a 100644 --- a/.tav.yml +++ b/.tav.yml @@ -512,15 +512,14 @@ aws-sdk: - node test/instrumentation/modules/aws-sdk/sqs.test.js - node test/instrumentation/modules/aws-sdk/dynamodb.test.js +# For all AWS-SDK clients want this version range: +# versions: '>=3.15.0 <4' +# However, @awk-sdk/client-* releases *very* frequently (almost every day) and there +# is no need to test *all* those releases. Instead we statically list a subset +# of versions to test. '@aws-sdk/client-s3': - # We want this version range: - # versions: '>=3.15.0 <4' - # However, @awk-sdk/client-s3 releases *very* frequently (almost every day) and there - # is no need to test *all* those releases. Instead we statically list a subset - # of versions to test. - # # Maintenance note: This should be updated periodically using: - # ./dev-utils/aws-sdk-s3-client-tav-versions.sh + # node ./dev-utils/tav-versions.js @aws-sdk/client-s3 ">=3.15.0 <4" # # Test v3.15.0, every N=37 of 191 releases, and current latest. versions: '3.15.0 || 3.48.0 || 3.112.0 || 3.188.0 || 3.257.0 || 3.315.0 || 3.325.0 || >3.325.0 <4' @@ -528,6 +527,16 @@ aws-sdk: - node test/instrumentation/modules/@aws-sdk/client-s3.test.js node: '>=14' +'@aws-sdk/client-dynamodb': + # Maintenance note: This should be updated periodically using: + # node ./dev-utils/tav-versions.js @aws-sdk/client-dynamodb ">=3.15.0 <4" + # + # Test v3.15.0, every N=41 of 210 releases, and current latest. + versions: '3.15.0 || 3.53.0 || 3.154.0 || 3.216.0 || 3.294.0 || 3.360.0 || 3.370.0 || >3.370.0 <4' + commands: + - node test/instrumentation/modules/@aws-sdk/client-s3.test.js + node: '>=14' + # - undici@4.7.0 added its diagnostics_channel support. # - In undici@4.7.1 the `request.origin` property was added, which we need # in the 'undici:request:create' diagnostic message. diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 067c57bcec..1e0c3c1a4c 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -40,6 +40,9 @@ Notes: [float] ===== Features +* Add support for `@aws-sdk/client-dynamodb`, one of the AWS SDK v3 clients. + ({pull}3486[#3486]) + [float] ===== Bug fixes diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index 29224652cc..f19a928e52 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -114,6 +114,7 @@ The Node.js agent will automatically instrument the following modules to give yo |Module |Version |Note |https://www.npmjs.com/package/aws-sdk[aws-sdk] |>=2.858.0 <3 |Will instrument SQS send/receive/delete messages, all S3 methods, all DynamoDB methods, and the SNS publish method |https://www.npmjs.com/package/@aws-sdk/client-s3[@aws-sdk/client-s3] |>=3.15.0 <4 |Will instrument all S3 methods +|https://www.npmjs.com/package/@aws-sdk/client-dynamodb[@aws-sdk/client-dynamodb] |>=3.15.0 <4 |Will instrument all DynamoDB methods |https://www.npmjs.com/package/cassandra-driver[cassandra-driver] |>=3.0.0 <5 |Will instrument all queries |https://www.npmjs.com/package/elasticsearch[elasticsearch] |>=8.0.0 |Will instrument all queries |https://www.npmjs.com/package/@elastic/elasticsearch[@elastic/elasticsearch] |>=7.0.0 <9.0.0 |Will instrument all queries diff --git a/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js b/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js new file mode 100644 index 0000000000..6c7532627f --- /dev/null +++ b/lib/instrumentation/modules/@aws-sdk/client-dynamodb.js @@ -0,0 +1,151 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict' + +const constants = require('../../../constants') +const NAME = 'DynamoDB' +const TYPE = 'db' +const SUBTYPE = 'dynamodb' +const elasticAPMStash = Symbol('elasticAPMStash') + +/** + * Returns middlewares to instrument an S3Client instance + * + * @param {import('@aws-sdk/client-dynamodb').DynamoDBClient} client + * @param {any} agent + * @returns {import('./smithy-client').AWSMiddlewareEntry[]} + */ +function dynamoDBMiddlewareFactory (client, agent) { + return [ + { + middleware: (next, context) => async (args) => { + // Ensure there is a span from the wrapped `client.send()`. + const span = agent._instrumentation.currSpan() + if (!span || !(span.type === TYPE && span.subtype === SUBTYPE)) { + return await next(args) + } + + const input = args.input + const table = input && input.TableName + // The given span comes with the operation name and we need to + // add the table if applies + if (table) { + span.name += ' ' + table + } + + let err + let result + let response + let statusCode + try { + result = await next(args) + response = result && result.response + statusCode = response && response.statusCode + } catch (ex) { + // Save the error for use in `finally` below, but re-throw it to + // not impact code flow. + err = ex + + // This code path happens with a GetObject conditional request + // that returns a 304 Not Modified. + statusCode = err && err.$metadata && err.$metadata.httpStatusCode + throw ex + } finally { + if (statusCode) { + span._setOutcomeFromHttpStatusCode(statusCode) + } else { + span._setOutcomeFromErrorCapture(constants.OUTCOME_FAILURE) + } + if (err && (!statusCode || statusCode >= 400)) { + agent.captureError(err, { skipOutcome: true }) + } + + // TODO: something related to region? + const config = client.config + const region = await config.region() + + // Set the db context + const dbContext = { type: SUBTYPE } // dynamodb + if (region) { + dbContext.instance = region + } + if (input && input.KeyConditionExpression) { + dbContext.statement = input.KeyConditionExpression + } + span.setDbContext(dbContext) + + // Set destination context + const destContext = {} + if (context[elasticAPMStash]) { + destContext.address = context[elasticAPMStash].hostname + destContext.port = context[elasticAPMStash].port + } + if (region) { + destContext.service = { resource: `dynamodb/${region}` } + destContext.cloud = { region } + } + span._setDestinationContext(destContext) + + // TODO: review spec and add OTel attributes + // https://github.com/open-telemetry/opentelemetry-specification/blob/v1.20.0/semantic_conventions/trace/instrumentation/aws-sdk.yml#L435 + // OTel attributes + // const otelAttrs = span._getOTelAttributes() + + // otelAttrs['aws.dynamodb.system'] = 'dynamodb' + // otelAttrs['aws.dynamodb.operation'] = span.action + // if (table) { + // otelAttrs['aws.dynamodb.table_names'] = table + // } + // if (input && input.Select) { + // otelAttrs['aws.dynamodb.select'] = input.Select + // } + // if (input && input.ProjectionExpression) { + // otelAttrs['aws.dynamodb.projection'] = input.ProjectionExpression + // } + // // TODO: check result for consumed_capacity + // if (result && result.ConsumedCapacity) { + // otelAttrs['aws.dynamodb.consumed_capacity'] = JSON.stringify(result.ConsumedCapacity) + // } + + span.end() + } + + return result + }, + options: { step: 'initialize', priority: 'high', name: 'elasticAPMSpan' } + }, + { + middleware: (next, context) => async (args) => { + const req = args.request + let port = req.port + + // Resolve port for HTTP(S) protocols + if (port === undefined) { + if (req.protocol === 'https:') { + port = 443 + } else if (req.protocol === 'http:') { + port = 80 + } + } + + context[elasticAPMStash] = { + hostname: req.hostname, + port + } + return next(args) + }, + options: { step: 'finalizeRequest', name: 'elasticAPMHTTPInfo' } + } + ] +} + +module.exports = { + DYNAMODB_NAME: NAME, + DYNAMODB_TYPE: TYPE, + DYNAMODB_SUBTYPE: SUBTYPE, + dynamoDBMiddlewareFactory +} diff --git a/lib/instrumentation/modules/@aws-sdk/smithy-client.js b/lib/instrumentation/modules/@aws-sdk/smithy-client.js index da83c8689c..7df8360018 100644 --- a/lib/instrumentation/modules/@aws-sdk/smithy-client.js +++ b/lib/instrumentation/modules/@aws-sdk/smithy-client.js @@ -15,6 +15,12 @@ const { S3_SUBTYPE, s3MiddlewareFactory } = require('./client-s3') +const { + DYNAMODB_NAME, + DYNAMODB_TYPE, + DYNAMODB_SUBTYPE, + dynamoDBMiddlewareFactory +} = require('./client-dynamodb') /** * We do alias them to a local type @@ -55,6 +61,12 @@ const clientsConfig = { TYPE: S3_TYPE, SUBTYPE: S3_SUBTYPE, factory: s3MiddlewareFactory + }, + DynamoDBClient: { + NAME: DYNAMODB_NAME, + TYPE: DYNAMODB_TYPE, + SUBTYPE: DYNAMODB_SUBTYPE, + factory: dynamoDBMiddlewareFactory } } diff --git a/package-lock.json b/package-lock.json index ba22187326..9ffb3d9c27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,7 @@ "original-url": "^1.2.3", "pino": "^6.11.2", "relative-microtime": "^2.0.0", - "require-in-the-middle": "^7.0.1", + "require-in-the-middle": "^7.1.1", "semver": "^6.3.1", "shallow-clone-shim": "^2.0.0", "source-map": "^0.8.0-beta.0", @@ -46,6 +46,7 @@ }, "devDependencies": { "@apollo/server": "^4.2.2", + "@aws-sdk/client-dynamodb": "^3.369.0", "@aws-sdk/client-s3": "^3.363.0", "@aws-sdk/s3-request-presigner": "^3.363.0", "@babel/cli": "^7.8.4", @@ -797,6 +798,470 @@ "tslib": "^2.5.0" } }, + "node_modules/@aws-sdk/client-dynamodb": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.370.0.tgz", + "integrity": "sha512-qL02C4adrZljZXgPonSHYJ95qkj1IlIsZx3NxaGzMc5S3uPJua14phktk2XP8K/Ap3gD6vLWt1UVVoo/rDFOAQ==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.370.0", + "@aws-sdk/credential-provider-node": "3.370.0", + "@aws-sdk/middleware-endpoint-discovery": "3.370.0", + "@aws-sdk/middleware-host-header": "3.370.0", + "@aws-sdk/middleware-logger": "3.370.0", + "@aws-sdk/middleware-recursion-detection": "3.370.0", + "@aws-sdk/middleware-signing": "3.370.0", + "@aws-sdk/middleware-user-agent": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@aws-sdk/util-user-agent-browser": "3.370.0", + "@aws-sdk/util-user-agent-node": "3.370.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.2", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "@smithy/util-waiter": "^1.0.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/client-sso": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.370.0.tgz", + "integrity": "sha512-0Ty1iHuzNxMQtN7nahgkZr4Wcu1XvqGfrQniiGdKKif9jG/4elxsQPiydRuQpFqN6b+bg7wPP7crFP1uTxx2KQ==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.370.0", + "@aws-sdk/middleware-logger": "3.370.0", + "@aws-sdk/middleware-recursion-detection": "3.370.0", + "@aws-sdk/middleware-user-agent": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@aws-sdk/util-user-agent-browser": "3.370.0", + "@aws-sdk/util-user-agent-node": "3.370.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.2", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.370.0.tgz", + "integrity": "sha512-jAYOO74lmVXylQylqkPrjLzxvUnMKw476JCUTvCO6Q8nv3LzCWd76Ihgv/m9Q4M2Tbqi1iP2roVK5bstsXzEjA==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.370.0", + "@aws-sdk/middleware-logger": "3.370.0", + "@aws-sdk/middleware-recursion-detection": "3.370.0", + "@aws-sdk/middleware-user-agent": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@aws-sdk/util-user-agent-browser": "3.370.0", + "@aws-sdk/util-user-agent-node": "3.370.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.2", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/client-sts": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.370.0.tgz", + "integrity": "sha512-utFxOPWIzbN+3kc415Je2o4J72hOLNhgR2Gt5EnRSggC3yOnkC4GzauxG8n7n5gZGBX45eyubHyPOXLOIyoqQA==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.370.0", + "@aws-sdk/middleware-host-header": "3.370.0", + "@aws-sdk/middleware-logger": "3.370.0", + "@aws-sdk/middleware-recursion-detection": "3.370.0", + "@aws-sdk/middleware-sdk-sts": "3.370.0", + "@aws-sdk/middleware-signing": "3.370.0", + "@aws-sdk/middleware-user-agent": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@aws-sdk/util-user-agent-browser": "3.370.0", + "@aws-sdk/util-user-agent-node": "3.370.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.2", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.370.0.tgz", + "integrity": "sha512-raR3yP/4GGbKFRPP5hUBNkEmTnzxI9mEc2vJAJrcv4G4J4i/UP6ELiLInQ5eO2/VcV/CeKGZA3t7d1tsJ+jhCg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.370.0.tgz", + "integrity": "sha512-eJyapFKa4NrC9RfTgxlXnXfS9InG/QMEUPPVL+VhG7YS6nKqetC1digOYgivnEeu+XSKE0DJ7uZuXujN2Y7VAQ==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.370.0", + "@aws-sdk/credential-provider-process": "3.370.0", + "@aws-sdk/credential-provider-sso": "3.370.0", + "@aws-sdk/credential-provider-web-identity": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.370.0.tgz", + "integrity": "sha512-gkFiotBFKE4Fcn8CzQnMeab9TAR06FEAD02T4ZRYW1xGrBJOowmje9dKqdwQFHSPgnWAP+8HoTA8iwbhTLvjNA==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.370.0", + "@aws-sdk/credential-provider-ini": "3.370.0", + "@aws-sdk/credential-provider-process": "3.370.0", + "@aws-sdk/credential-provider-sso": "3.370.0", + "@aws-sdk/credential-provider-web-identity": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.370.0.tgz", + "integrity": "sha512-0BKFFZmUO779Xdw3u7wWnoWhYA4zygxJbgGVSyjkOGBvdkbPSTTcdwT1KFkaQy2kOXYeZPl+usVVRXs+ph4ejg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.370.0.tgz", + "integrity": "sha512-PFroYm5hcPSfC/jkZnCI34QFL3I7WVKveVk6/F3fud/cnP8hp6YjA9NiTNbqdFSzsyoiN/+e5fZgNKih8vVPTA==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso": "3.370.0", + "@aws-sdk/token-providers": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.370.0.tgz", + "integrity": "sha512-CFaBMLRudwhjv1sDzybNV93IaT85IwS+L8Wq6VRMa0mro1q9rrWsIZO811eF+k0NEPfgU1dLH+8Vc2qhw4SARQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.370.0.tgz", + "integrity": "sha512-CPXOm/TnOFC7KyXcJglICC7OiA7Kj6mT3ChvEijr56TFOueNHvJdV4aNIFEQy0vGHOWtY12qOWLNto/wYR1BAQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-logger": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.370.0.tgz", + "integrity": "sha512-cQMq9SaZ/ORmTJPCT6VzMML7OxFdQzNkhMAgKpTDl+tdPWynlHF29E5xGoSzROnThHlQPCjogU0NZ8AxI0SWPA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.370.0.tgz", + "integrity": "sha512-L7ZF/w0lAAY/GK1khT8VdoU0XB7nWHk51rl/ecAg64J70dHnMOAg8n+5FZ9fBu/xH1FwUlHOkwlodJOgzLJjtg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.370.0.tgz", + "integrity": "sha512-ykbsoVy0AJtVbuhAlTAMcaz/tCE3pT8nAp0L7CQQxSoanRCvOux7au0KwMIQVhxgnYid4dWVF6d00SkqU5MXRA==", + "dev": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-signing": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.370.0.tgz", + "integrity": "sha512-Dwr/RTCWOXdm394wCwICGT2VNOTMRe4IGPsBRJAsM24pm+EEqQzSS3Xu/U/zF4exuxqpMta4wec4QpSarPNTxA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/protocol-http": "^1.1.0", + "@smithy/signature-v4": "^1.0.1", + "@smithy/types": "^1.1.0", + "@smithy/util-middleware": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.370.0.tgz", + "integrity": "sha512-2+3SB6MtMAq1+gVXhw0Y3ONXuljorh6ijnxgTpv+uQnBW5jHCUiAS8WDYiDEm7i9euJPbvJfM8WUrSMDMU6Cog==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/token-providers": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.370.0.tgz", + "integrity": "sha512-EyR2ZYr+lJeRiZU2/eLR+mlYU9RXLQvNyGFSAekJKgN13Rpq/h0syzXVFLP/RSod/oZenh/fhVZ2HwlZxuGBtQ==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso-oidc": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", + "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", + "dev": true, + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/util-endpoints": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.370.0.tgz", + "integrity": "sha512-5ltVAnM79nRlywwzZN5i8Jp4tk245OCGkKwwXbnDU+gq7zT3CIOsct1wNZvmpfZEPGt/bv7/NyRcjP+7XNsX/g==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.370.0.tgz", + "integrity": "sha512-028LxYZMQ0DANKhW+AKFQslkScZUeYlPmSphrCIXgdIItRZh6ZJHGzE7J/jDsEntZOrZJsjI4z0zZ5W2idj04w==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/types": "^1.1.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.370.0.tgz", + "integrity": "sha512-33vxZUp8vxTT/DGYIR3PivQm07sSRGWI+4fCv63Rt7Q++fO24E0kQtmVAlikRY810I10poD6rwILVtITtFSzkg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.370.0", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@aws-sdk/client-s3": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.363.0.tgz", @@ -1105,6 +1570,19 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/endpoint-cache": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.310.0.tgz", + "integrity": "sha512-y3wipforet41EDTI0vnzxILqwAGll1KfI5qcdX9pXF/WF1f+3frcOtPiWtQEZQpy4czRogKm3BHo70QBYAZxlQ==", + "dev": true, + "dependencies": { + "mnemonist": "0.38.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/hash-blob-browser": { "version": "3.357.0", "resolved": "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.357.0.tgz", @@ -1170,6 +1648,35 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/middleware-endpoint-discovery": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.370.0.tgz", + "integrity": "sha512-CxTo+AUW1XIT0U1COzBjdQ39eWbWk5Sv9OmA73g1vHwwvy9M3vFl78cnaKnauGv7BI/NtM5IeRy0w5T22BtWgg==", + "dev": true, + "dependencies": { + "@aws-sdk/endpoint-cache": "3.310.0", + "@aws-sdk/types": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint-discovery/node_modules/@aws-sdk/types": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", + "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", + "dev": true, + "dependencies": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/middleware-expect-continue": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.363.0.tgz", @@ -5289,15 +5796,15 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.0.1.tgz", - "integrity": "sha512-XEHPq73PPm0sXul//5MRC8JKDwHTaNjV3myd9FJ5YoUT+QMJ27gqiwwAMVASJ6vC4ELHzgQdmoTZxVX83Jx17w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.0.3.tgz", + "integrity": "sha512-GsWvTXMFjSgl617PCE2km//kIjjtvMRrR2GAuRDIS9sHiLwmkS46VWaVYy+XE7ubEsEtzZ5yK2e8TKDR6Qr5Lw==", "dev": true, "dependencies": { - "@smithy/middleware-serde": "^1.0.1", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-middleware": "^1.0.1", + "@smithy/middleware-serde": "^1.0.2", + "@smithy/types": "^1.1.1", + "@smithy/url-parser": "^1.0.2", + "@smithy/util-middleware": "^1.0.2", "tslib": "^2.5.0" }, "engines": { @@ -5305,16 +5812,16 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.0.2.tgz", - "integrity": "sha512-Osj+XsqJ67RuX1MLc3+7LDtlRz8eNVqr/Pim9yPAjRa03Xberu6ST3a3oV3NqRpcUgMTifxuaKVIciuPE3rTWA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.0.4.tgz", + "integrity": "sha512-G7uRXGFL8c3F7APnoIMTtNAHH8vT4F2qVnAWGAZaervjupaUQuRRHYBLYubK0dWzOZz86BtAXKieJ5p+Ni2Xpg==", "dev": true, "dependencies": { - "@smithy/protocol-http": "^1.1.0", - "@smithy/service-error-classification": "^1.0.1", - "@smithy/types": "^1.1.0", - "@smithy/util-middleware": "^1.0.1", - "@smithy/util-retry": "^1.0.2", + "@smithy/protocol-http": "^1.1.1", + "@smithy/service-error-classification": "^1.0.3", + "@smithy/types": "^1.1.1", + "@smithy/util-middleware": "^1.0.2", + "@smithy/util-retry": "^1.0.4", "tslib": "^2.5.0", "uuid": "^8.3.2" }, @@ -5323,12 +5830,12 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.0.1.tgz", - "integrity": "sha512-bn5lWk8UUeXFCQfkrNErz5SbeNd+2hgYegHMLsOLPt4URDIsyREar6wMsdsR+8UCdgR5s8udG3Zalgc7puizIQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.0.2.tgz", + "integrity": "sha512-T4PcdMZF4xme6koUNfjmSZ1MLi7eoFeYCtodQNQpBNsS77TuJt1A6kt5kP/qxrTvfZHyFlj0AubACoaUqgzPeg==", "dev": true, "dependencies": { - "@smithy/types": "^1.1.0", + "@smithy/types": "^1.1.1", "tslib": "^2.5.0" }, "engines": { @@ -5392,12 +5899,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.0.tgz", - "integrity": "sha512-H5y/kZOqfJSqRkwtcAoVbqONmhdXwSgYNJ1Glk5Ry8qlhVVy5qUzD9EklaCH8/XLnoCsLO/F/Giee8MIvaBRkg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.1.tgz", + "integrity": "sha512-mFLFa2sSvlUxm55U7B4YCIsJJIMkA6lHxwwqOaBkral1qxFz97rGffP/mmd4JDuin1EnygiO5eNJGgudiUgmDQ==", "dev": true, "dependencies": { - "@smithy/types": "^1.1.0", + "@smithy/types": "^1.1.1", "tslib": "^2.5.0" }, "engines": { @@ -5419,12 +5926,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.0.1.tgz", - "integrity": "sha512-zauxdMc3cwxoLitI5DZqH7xN6Fk0mwRxrUMAETbav2j6Se2U0UGak/55rZcDg2yGzOURaLYi5iOm1gHr98P+Bw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.0.2.tgz", + "integrity": "sha512-IWxwxjn+KHWRRRB+K2Ngl+plTwo2WSgc2w+DvLy0DQZJh9UGOpw40d6q97/63GBlXIt4TEt5NbcFrO30CKlrsA==", "dev": true, "dependencies": { - "@smithy/types": "^1.1.0", + "@smithy/types": "^1.1.1", "tslib": "^2.5.0" }, "engines": { @@ -5432,9 +5939,9 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.0.1.tgz", - "integrity": "sha512-EcFVOHFv4YZHUrJ6161tpPKXNlQsTAQKoKorODxuOQW1my14mgoyiiMXrlnSMaYRF//oaONYfFOQ2EMAUTC5DQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.0.3.tgz", + "integrity": "sha512-2eglIYqrtcUnuI71yweu7rSfCgt6kVvRVf0C72VUqrd0LrV1M0BM0eYN+nitp2CHPSdmMI96pi+dU9U/UqAMSA==", "dev": true, "engines": { "node": ">=14.0.0" @@ -5488,9 +5995,9 @@ } }, "node_modules/@smithy/types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.0.tgz", - "integrity": "sha512-KzmvisMmuwD2jZXuC9e65JrgsZM97y5NpDU7g347oB+Q+xQLU6hQZ5zFNNbEfwwOJHoOvEVTna+dk1h/lW7alw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.1.tgz", + "integrity": "sha512-tMpkreknl2gRrniHeBtdgQwaOlo39df8RxSrwsHVNIGXULy5XP6KqgScUw2m12D15wnJCKWxVhCX+wbrBW/y7g==", "dev": true, "dependencies": { "tslib": "^2.5.0" @@ -5500,13 +6007,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.0.1.tgz", - "integrity": "sha512-33vWEtE6HzmwjEcEb4I58XMLRAchwPS93YhfDyXAXr1jwDCzfXmMayQwwpyW847rpWj0XJimxqia8q0z+k/ybw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.0.2.tgz", + "integrity": "sha512-0JRsDMQe53F6EHRWksdcavKDRjyqp8vrjakg8EcCUOa7PaFRRB1SO/xGZdzSlW1RSTWQDEksFMTCEcVEKmAoqA==", "dev": true, "dependencies": { - "@smithy/querystring-parser": "^1.0.1", - "@smithy/types": "^1.1.0", + "@smithy/querystring-parser": "^1.0.2", + "@smithy/types": "^1.1.1", "tslib": "^2.5.0" } }, @@ -5614,9 +6121,9 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.0.1.tgz", - "integrity": "sha512-u9akN3Zmbr0vZH4F+2iehG7cFg+3fvDfnvS/hhsXH4UHuhqiQ+ADefibnLzPoz1pooY7rvwaQ/TVHyJmZHdLdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.0.2.tgz", + "integrity": "sha512-vtXK7GOR2BoseCX8NCGe9SaiZrm9M2lm/RVexFGyPuafTtry9Vyv7hq/vw8ifd/G/pSJ+msByfJVb1642oQHKw==", "dev": true, "dependencies": { "tslib": "^2.5.0" @@ -5626,12 +6133,12 @@ } }, "node_modules/@smithy/util-retry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.0.2.tgz", - "integrity": "sha512-LqzAy1QzeAHpSZpsd2LtKZRbco7F5gF+Zpoa4TuuFq9f+CGmH6OuTCIN3I13LPEBa4zs2AgvGnXljvMbUw5WGw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.0.4.tgz", + "integrity": "sha512-RnZPVFvRoqdj2EbroDo3OsnnQU8eQ4AlnZTOGusbYKybH3269CFdrZfZJloe60AQjX7di3J6t/79PjwCLO5Khw==", "dev": true, "dependencies": { - "@smithy/service-error-classification": "^1.0.1", + "@smithy/service-error-classification": "^1.0.3", "tslib": "^2.5.0" }, "engines": { @@ -12740,6 +13247,15 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "dev": true, + "dependencies": { + "obliterator": "^1.6.1" + } + }, "node_modules/module-details-from-path": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", @@ -13464,6 +13980,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", + "dev": true + }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -17929,6 +18451,404 @@ "tslib": "^2.5.0" } }, + "@aws-sdk/client-dynamodb": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.370.0.tgz", + "integrity": "sha512-qL02C4adrZljZXgPonSHYJ95qkj1IlIsZx3NxaGzMc5S3uPJua14phktk2XP8K/Ap3gD6vLWt1UVVoo/rDFOAQ==", + "dev": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.370.0", + "@aws-sdk/credential-provider-node": "3.370.0", + "@aws-sdk/middleware-endpoint-discovery": "3.370.0", + "@aws-sdk/middleware-host-header": "3.370.0", + "@aws-sdk/middleware-logger": "3.370.0", + "@aws-sdk/middleware-recursion-detection": "3.370.0", + "@aws-sdk/middleware-signing": "3.370.0", + "@aws-sdk/middleware-user-agent": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@aws-sdk/util-user-agent-browser": "3.370.0", + "@aws-sdk/util-user-agent-node": "3.370.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.2", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "@smithy/util-waiter": "^1.0.1", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "@aws-sdk/client-sso": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.370.0.tgz", + "integrity": "sha512-0Ty1iHuzNxMQtN7nahgkZr4Wcu1XvqGfrQniiGdKKif9jG/4elxsQPiydRuQpFqN6b+bg7wPP7crFP1uTxx2KQ==", + "dev": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.370.0", + "@aws-sdk/middleware-logger": "3.370.0", + "@aws-sdk/middleware-recursion-detection": "3.370.0", + "@aws-sdk/middleware-user-agent": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@aws-sdk/util-user-agent-browser": "3.370.0", + "@aws-sdk/util-user-agent-node": "3.370.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.2", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sso-oidc": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.370.0.tgz", + "integrity": "sha512-jAYOO74lmVXylQylqkPrjLzxvUnMKw476JCUTvCO6Q8nv3LzCWd76Ihgv/m9Q4M2Tbqi1iP2roVK5bstsXzEjA==", + "dev": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.370.0", + "@aws-sdk/middleware-logger": "3.370.0", + "@aws-sdk/middleware-recursion-detection": "3.370.0", + "@aws-sdk/middleware-user-agent": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@aws-sdk/util-user-agent-browser": "3.370.0", + "@aws-sdk/util-user-agent-node": "3.370.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.2", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/client-sts": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.370.0.tgz", + "integrity": "sha512-utFxOPWIzbN+3kc415Je2o4J72hOLNhgR2Gt5EnRSggC3yOnkC4GzauxG8n7n5gZGBX45eyubHyPOXLOIyoqQA==", + "dev": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.370.0", + "@aws-sdk/middleware-host-header": "3.370.0", + "@aws-sdk/middleware-logger": "3.370.0", + "@aws-sdk/middleware-recursion-detection": "3.370.0", + "@aws-sdk/middleware-sdk-sts": "3.370.0", + "@aws-sdk/middleware-signing": "3.370.0", + "@aws-sdk/middleware-user-agent": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@aws-sdk/util-user-agent-browser": "3.370.0", + "@aws-sdk/util-user-agent-node": "3.370.0", + "@smithy/config-resolver": "^1.0.1", + "@smithy/fetch-http-handler": "^1.0.1", + "@smithy/hash-node": "^1.0.1", + "@smithy/invalid-dependency": "^1.0.1", + "@smithy/middleware-content-length": "^1.0.1", + "@smithy/middleware-endpoint": "^1.0.2", + "@smithy/middleware-retry": "^1.0.3", + "@smithy/middleware-serde": "^1.0.1", + "@smithy/middleware-stack": "^1.0.1", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/node-http-handler": "^1.0.2", + "@smithy/protocol-http": "^1.1.0", + "@smithy/smithy-client": "^1.0.3", + "@smithy/types": "^1.1.0", + "@smithy/url-parser": "^1.0.1", + "@smithy/util-base64": "^1.0.1", + "@smithy/util-body-length-browser": "^1.0.1", + "@smithy/util-body-length-node": "^1.0.1", + "@smithy/util-defaults-mode-browser": "^1.0.1", + "@smithy/util-defaults-mode-node": "^1.0.1", + "@smithy/util-retry": "^1.0.3", + "@smithy/util-utf8": "^1.0.1", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.370.0.tgz", + "integrity": "sha512-raR3yP/4GGbKFRPP5hUBNkEmTnzxI9mEc2vJAJrcv4G4J4i/UP6ELiLInQ5eO2/VcV/CeKGZA3t7d1tsJ+jhCg==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.370.0.tgz", + "integrity": "sha512-eJyapFKa4NrC9RfTgxlXnXfS9InG/QMEUPPVL+VhG7YS6nKqetC1digOYgivnEeu+XSKE0DJ7uZuXujN2Y7VAQ==", + "dev": true, + "requires": { + "@aws-sdk/credential-provider-env": "3.370.0", + "@aws-sdk/credential-provider-process": "3.370.0", + "@aws-sdk/credential-provider-sso": "3.370.0", + "@aws-sdk/credential-provider-web-identity": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.370.0.tgz", + "integrity": "sha512-gkFiotBFKE4Fcn8CzQnMeab9TAR06FEAD02T4ZRYW1xGrBJOowmje9dKqdwQFHSPgnWAP+8HoTA8iwbhTLvjNA==", + "dev": true, + "requires": { + "@aws-sdk/credential-provider-env": "3.370.0", + "@aws-sdk/credential-provider-ini": "3.370.0", + "@aws-sdk/credential-provider-process": "3.370.0", + "@aws-sdk/credential-provider-sso": "3.370.0", + "@aws-sdk/credential-provider-web-identity": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/credential-provider-imds": "^1.0.1", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.370.0.tgz", + "integrity": "sha512-0BKFFZmUO779Xdw3u7wWnoWhYA4zygxJbgGVSyjkOGBvdkbPSTTcdwT1KFkaQy2kOXYeZPl+usVVRXs+ph4ejg==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.370.0.tgz", + "integrity": "sha512-PFroYm5hcPSfC/jkZnCI34QFL3I7WVKveVk6/F3fud/cnP8hp6YjA9NiTNbqdFSzsyoiN/+e5fZgNKih8vVPTA==", + "dev": true, + "requires": { + "@aws-sdk/client-sso": "3.370.0", + "@aws-sdk/token-providers": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.370.0.tgz", + "integrity": "sha512-CFaBMLRudwhjv1sDzybNV93IaT85IwS+L8Wq6VRMa0mro1q9rrWsIZO811eF+k0NEPfgU1dLH+8Vc2qhw4SARQ==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.370.0.tgz", + "integrity": "sha512-CPXOm/TnOFC7KyXcJglICC7OiA7Kj6mT3ChvEijr56TFOueNHvJdV4aNIFEQy0vGHOWtY12qOWLNto/wYR1BAQ==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.370.0.tgz", + "integrity": "sha512-cQMq9SaZ/ORmTJPCT6VzMML7OxFdQzNkhMAgKpTDl+tdPWynlHF29E5xGoSzROnThHlQPCjogU0NZ8AxI0SWPA==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.370.0.tgz", + "integrity": "sha512-L7ZF/w0lAAY/GK1khT8VdoU0XB7nWHk51rl/ecAg64J70dHnMOAg8n+5FZ9fBu/xH1FwUlHOkwlodJOgzLJjtg==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-sdk-sts": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.370.0.tgz", + "integrity": "sha512-ykbsoVy0AJtVbuhAlTAMcaz/tCE3pT8nAp0L7CQQxSoanRCvOux7au0KwMIQVhxgnYid4dWVF6d00SkqU5MXRA==", + "dev": true, + "requires": { + "@aws-sdk/middleware-signing": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-signing": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.370.0.tgz", + "integrity": "sha512-Dwr/RTCWOXdm394wCwICGT2VNOTMRe4IGPsBRJAsM24pm+EEqQzSS3Xu/U/zF4exuxqpMta4wec4QpSarPNTxA==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/protocol-http": "^1.1.0", + "@smithy/signature-v4": "^1.0.1", + "@smithy/types": "^1.1.0", + "@smithy/util-middleware": "^1.0.1", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.370.0.tgz", + "integrity": "sha512-2+3SB6MtMAq1+gVXhw0Y3ONXuljorh6ijnxgTpv+uQnBW5jHCUiAS8WDYiDEm7i9euJPbvJfM8WUrSMDMU6Cog==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@aws-sdk/util-endpoints": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/token-providers": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.370.0.tgz", + "integrity": "sha512-EyR2ZYr+lJeRiZU2/eLR+mlYU9RXLQvNyGFSAekJKgN13Rpq/h0syzXVFLP/RSod/oZenh/fhVZ2HwlZxuGBtQ==", + "dev": true, + "requires": { + "@aws-sdk/client-sso-oidc": "3.370.0", + "@aws-sdk/types": "3.370.0", + "@smithy/property-provider": "^1.0.1", + "@smithy/shared-ini-file-loader": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/types": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", + "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", + "dev": true, + "requires": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-endpoints": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.370.0.tgz", + "integrity": "sha512-5ltVAnM79nRlywwzZN5i8Jp4tk245OCGkKwwXbnDU+gq7zT3CIOsct1wNZvmpfZEPGt/bv7/NyRcjP+7XNsX/g==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.370.0.tgz", + "integrity": "sha512-028LxYZMQ0DANKhW+AKFQslkScZUeYlPmSphrCIXgdIItRZh6ZJHGzE7J/jDsEntZOrZJsjI4z0zZ5W2idj04w==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@smithy/types": "^1.1.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.370.0.tgz", + "integrity": "sha512-33vxZUp8vxTT/DGYIR3PivQm07sSRGWI+4fCv63Rt7Q++fO24E0kQtmVAlikRY810I10poD6rwILVtITtFSzkg==", + "dev": true, + "requires": { + "@aws-sdk/types": "3.370.0", + "@smithy/node-config-provider": "^1.0.1", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + } + } + }, "@aws-sdk/client-s3": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.363.0.tgz", @@ -18207,6 +19127,16 @@ "tslib": "^2.5.0" } }, + "@aws-sdk/endpoint-cache": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.310.0.tgz", + "integrity": "sha512-y3wipforet41EDTI0vnzxILqwAGll1KfI5qcdX9pXF/WF1f+3frcOtPiWtQEZQpy4czRogKm3BHo70QBYAZxlQ==", + "dev": true, + "requires": { + "mnemonist": "0.38.3", + "tslib": "^2.5.0" + } + }, "@aws-sdk/hash-blob-browser": { "version": "3.357.0", "resolved": "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.357.0.tgz", @@ -18263,6 +19193,31 @@ "tslib": "^2.5.0" } }, + "@aws-sdk/middleware-endpoint-discovery": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.370.0.tgz", + "integrity": "sha512-CxTo+AUW1XIT0U1COzBjdQ39eWbWk5Sv9OmA73g1vHwwvy9M3vFl78cnaKnauGv7BI/NtM5IeRy0w5T22BtWgg==", + "dev": true, + "requires": { + "@aws-sdk/endpoint-cache": "3.310.0", + "@aws-sdk/types": "3.370.0", + "@smithy/protocol-http": "^1.1.0", + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "@aws-sdk/types": { + "version": "3.370.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.370.0.tgz", + "integrity": "sha512-8PGMKklSkRKjunFhzM2y5Jm0H2TBu7YRNISdYzXLUHKSP9zlMEYagseKVdmox0zKHf1LXVNuSlUV2b6SRrieCQ==", + "dev": true, + "requires": { + "@smithy/types": "^1.1.0", + "tslib": "^2.5.0" + } + } + } + }, "@aws-sdk/middleware-expect-continue": { "version": "3.363.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.363.0.tgz", @@ -21532,40 +22487,40 @@ } }, "@smithy/middleware-endpoint": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.0.1.tgz", - "integrity": "sha512-XEHPq73PPm0sXul//5MRC8JKDwHTaNjV3myd9FJ5YoUT+QMJ27gqiwwAMVASJ6vC4ELHzgQdmoTZxVX83Jx17w==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-1.0.3.tgz", + "integrity": "sha512-GsWvTXMFjSgl617PCE2km//kIjjtvMRrR2GAuRDIS9sHiLwmkS46VWaVYy+XE7ubEsEtzZ5yK2e8TKDR6Qr5Lw==", "dev": true, "requires": { - "@smithy/middleware-serde": "^1.0.1", - "@smithy/types": "^1.1.0", - "@smithy/url-parser": "^1.0.1", - "@smithy/util-middleware": "^1.0.1", + "@smithy/middleware-serde": "^1.0.2", + "@smithy/types": "^1.1.1", + "@smithy/url-parser": "^1.0.2", + "@smithy/util-middleware": "^1.0.2", "tslib": "^2.5.0" } }, "@smithy/middleware-retry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.0.2.tgz", - "integrity": "sha512-Osj+XsqJ67RuX1MLc3+7LDtlRz8eNVqr/Pim9yPAjRa03Xberu6ST3a3oV3NqRpcUgMTifxuaKVIciuPE3rTWA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-1.0.4.tgz", + "integrity": "sha512-G7uRXGFL8c3F7APnoIMTtNAHH8vT4F2qVnAWGAZaervjupaUQuRRHYBLYubK0dWzOZz86BtAXKieJ5p+Ni2Xpg==", "dev": true, "requires": { - "@smithy/protocol-http": "^1.1.0", - "@smithy/service-error-classification": "^1.0.1", - "@smithy/types": "^1.1.0", - "@smithy/util-middleware": "^1.0.1", - "@smithy/util-retry": "^1.0.2", + "@smithy/protocol-http": "^1.1.1", + "@smithy/service-error-classification": "^1.0.3", + "@smithy/types": "^1.1.1", + "@smithy/util-middleware": "^1.0.2", + "@smithy/util-retry": "^1.0.4", "tslib": "^2.5.0", "uuid": "^8.3.2" } }, "@smithy/middleware-serde": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.0.1.tgz", - "integrity": "sha512-bn5lWk8UUeXFCQfkrNErz5SbeNd+2hgYegHMLsOLPt4URDIsyREar6wMsdsR+8UCdgR5s8udG3Zalgc7puizIQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-1.0.2.tgz", + "integrity": "sha512-T4PcdMZF4xme6koUNfjmSZ1MLi7eoFeYCtodQNQpBNsS77TuJt1A6kt5kP/qxrTvfZHyFlj0AubACoaUqgzPeg==", "dev": true, "requires": { - "@smithy/types": "^1.1.0", + "@smithy/types": "^1.1.1", "tslib": "^2.5.0" } }, @@ -21614,12 +22569,12 @@ } }, "@smithy/protocol-http": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.0.tgz", - "integrity": "sha512-H5y/kZOqfJSqRkwtcAoVbqONmhdXwSgYNJ1Glk5Ry8qlhVVy5qUzD9EklaCH8/XLnoCsLO/F/Giee8MIvaBRkg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.1.1.tgz", + "integrity": "sha512-mFLFa2sSvlUxm55U7B4YCIsJJIMkA6lHxwwqOaBkral1qxFz97rGffP/mmd4JDuin1EnygiO5eNJGgudiUgmDQ==", "dev": true, "requires": { - "@smithy/types": "^1.1.0", + "@smithy/types": "^1.1.1", "tslib": "^2.5.0" } }, @@ -21635,19 +22590,19 @@ } }, "@smithy/querystring-parser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.0.1.tgz", - "integrity": "sha512-zauxdMc3cwxoLitI5DZqH7xN6Fk0mwRxrUMAETbav2j6Se2U0UGak/55rZcDg2yGzOURaLYi5iOm1gHr98P+Bw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-1.0.2.tgz", + "integrity": "sha512-IWxwxjn+KHWRRRB+K2Ngl+plTwo2WSgc2w+DvLy0DQZJh9UGOpw40d6q97/63GBlXIt4TEt5NbcFrO30CKlrsA==", "dev": true, "requires": { - "@smithy/types": "^1.1.0", + "@smithy/types": "^1.1.1", "tslib": "^2.5.0" } }, "@smithy/service-error-classification": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.0.1.tgz", - "integrity": "sha512-EcFVOHFv4YZHUrJ6161tpPKXNlQsTAQKoKorODxuOQW1my14mgoyiiMXrlnSMaYRF//oaONYfFOQ2EMAUTC5DQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-1.0.3.tgz", + "integrity": "sha512-2eglIYqrtcUnuI71yweu7rSfCgt6kVvRVf0C72VUqrd0LrV1M0BM0eYN+nitp2CHPSdmMI96pi+dU9U/UqAMSA==", "dev": true }, "@smithy/shared-ini-file-loader": { @@ -21689,22 +22644,22 @@ } }, "@smithy/types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.0.tgz", - "integrity": "sha512-KzmvisMmuwD2jZXuC9e65JrgsZM97y5NpDU7g347oB+Q+xQLU6hQZ5zFNNbEfwwOJHoOvEVTna+dk1h/lW7alw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.1.1.tgz", + "integrity": "sha512-tMpkreknl2gRrniHeBtdgQwaOlo39df8RxSrwsHVNIGXULy5XP6KqgScUw2m12D15wnJCKWxVhCX+wbrBW/y7g==", "dev": true, "requires": { "tslib": "^2.5.0" } }, "@smithy/url-parser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.0.1.tgz", - "integrity": "sha512-33vWEtE6HzmwjEcEb4I58XMLRAchwPS93YhfDyXAXr1jwDCzfXmMayQwwpyW847rpWj0XJimxqia8q0z+k/ybw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-1.0.2.tgz", + "integrity": "sha512-0JRsDMQe53F6EHRWksdcavKDRjyqp8vrjakg8EcCUOa7PaFRRB1SO/xGZdzSlW1RSTWQDEksFMTCEcVEKmAoqA==", "dev": true, "requires": { - "@smithy/querystring-parser": "^1.0.1", - "@smithy/types": "^1.1.0", + "@smithy/querystring-parser": "^1.0.2", + "@smithy/types": "^1.1.1", "tslib": "^2.5.0" } }, @@ -21791,21 +22746,21 @@ } }, "@smithy/util-middleware": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.0.1.tgz", - "integrity": "sha512-u9akN3Zmbr0vZH4F+2iehG7cFg+3fvDfnvS/hhsXH4UHuhqiQ+ADefibnLzPoz1pooY7rvwaQ/TVHyJmZHdLdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.0.2.tgz", + "integrity": "sha512-vtXK7GOR2BoseCX8NCGe9SaiZrm9M2lm/RVexFGyPuafTtry9Vyv7hq/vw8ifd/G/pSJ+msByfJVb1642oQHKw==", "dev": true, "requires": { "tslib": "^2.5.0" } }, "@smithy/util-retry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.0.2.tgz", - "integrity": "sha512-LqzAy1QzeAHpSZpsd2LtKZRbco7F5gF+Zpoa4TuuFq9f+CGmH6OuTCIN3I13LPEBa4zs2AgvGnXljvMbUw5WGw==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-1.0.4.tgz", + "integrity": "sha512-RnZPVFvRoqdj2EbroDo3OsnnQU8eQ4AlnZTOGusbYKybH3269CFdrZfZJloe60AQjX7di3J6t/79PjwCLO5Khw==", "dev": true, "requires": { - "@smithy/service-error-classification": "^1.0.1", + "@smithy/service-error-classification": "^1.0.3", "tslib": "^2.5.0" } }, @@ -27324,6 +28279,15 @@ "minimist": "^1.2.6" } }, + "mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "dev": true, + "requires": { + "obliterator": "^1.6.1" + } + }, "module-details-from-path": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", @@ -27898,6 +28862,12 @@ "es-abstract": "^1.20.4" } }, + "obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", + "dev": true + }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", diff --git a/package.json b/package.json index adb49415a7..8823c54a40 100644 --- a/package.json +++ b/package.json @@ -122,6 +122,7 @@ }, "devDependencies": { "@apollo/server": "^4.2.2", + "@aws-sdk/client-dynamodb": "^3.369.0", "@aws-sdk/client-s3": "^3.363.0", "@aws-sdk/s3-request-presigner": "^3.363.0", "@babel/cli": "^7.8.4", diff --git a/test/_utils.js b/test/_utils.js index b7ae19e91d..523c14f0a5 100644 --- a/test/_utils.js +++ b/test/_utils.js @@ -140,6 +140,28 @@ function sortApmEvents (events) { }) } +/** + * Slurp everything from the given ReadableStream and return the content, + * converted to a string. + */ +async function slurpStream (stream) { + return new Promise((resolve, reject) => { + const chunks = [] + stream.on('error', (err) => { + reject(err) + }) + stream.on('readable', function () { + let chunk + while ((chunk = this.read()) !== null) { + chunks.push(chunk) + } + }) + stream.on('end', () => { + resolve(Buffer.concat(chunks).toString('utf8')) + }) + }) +} + function quoteArg (a) { if (a.includes("'")) { return "'" + a.replace("'", "'\\''") + "'" @@ -346,6 +368,7 @@ module.exports = { findObjsInArray, formatForTComment, safeGetPackageVersion, + slurpStream, sortApmEvents, runTestFixtures } diff --git a/test/instrumentation/modules/@aws-sdk/client-dynamodb.test.js b/test/instrumentation/modules/@aws-sdk/client-dynamodb.test.js new file mode 100644 index 0000000000..d04dd9cf4c --- /dev/null +++ b/test/instrumentation/modules/@aws-sdk/client-dynamodb.test.js @@ -0,0 +1,262 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict' + +// Test DynamoDB instrumentation of the '@aws-sdk/client-dynamodb' module. +// +// Note that this uses localstack for testing, which mimicks the DynamoDB API but +// isn't identical. Some known limitations: +// TODO: check limitations + +const semver = require('semver') +if (process.env.GITHUB_ACTIONS === 'true' && process.platform === 'win32') { + console.log('# SKIP: GH Actions do not support docker services on Windows') + process.exit(0) +} +if (process.env.ELASTIC_APM_CONTEXT_MANAGER === 'patch') { + console.log('# SKIP @aws-sdk/* instrumentation does not work with contextManager="patch"') + process.exit() +} +if (semver.lt(process.version, '14.0.0')) { + console.log(`# SKIP @aws-sdk min supported node is v14 (node ${process.version})`) + process.exit() +} + +const test = require('tape') + +const { validateSpan } = require('../../../_validate_schema') +const { runTestFixtures, sortApmEvents } = require('../../../_utils') +// const { NODE_VER_RANGE_IITM } = require('../../../testconsts') +const AWS_REGION = 'us-east-2' +const LOCALSTACK_HOST = process.env.LOCALSTACK_HOST || 'localhost' +const endpoint = 'http://' + LOCALSTACK_HOST + ':4566' + +const testFixtures = [ + { + name: 'simple DynamoDB V3 usage scenario', + script: 'fixtures/use-client-dynamodb.js', + cwd: __dirname, + timeout: 20000, // sanity guard on the test hanging + maxBuffer: 10 * 1024 * 1024, // This is big, but I don't ever want this to be a failure reason. + env: { + AWS_ACCESS_KEY_ID: 'fake', + AWS_SECRET_ACCESS_KEY: 'fake', + TEST_TABLE_NAME: 'elasticapmtest-table-3', + TEST_ENDPOINT: endpoint, + TEST_REGION: AWS_REGION + }, + verbose: false, + checkApmServer: (t, apmServer) => { + t.ok(apmServer.events[0].metadata, 'metadata') + const events = sortApmEvents(apmServer.events) + + // First the transaction. + t.ok(events[0].transaction, 'got the transaction') + const tx = events.shift().transaction + const errors = events.filter(e => e.error).map(e => e.error) + + // Compare some common fields across all spans. + const spans = events.filter(e => e.span) + .map(e => e.span) + spans.forEach(s => { + const errs = validateSpan(s) + t.equal(errs, null, 'span is valid (per apm-server intake schema)') + }) + t.equal(spans.filter(s => s.trace_id === tx.trace_id).length, + spans.length, 'all spans have the same trace_id') + t.equal(spans.filter(s => s.transaction_id === tx.id).length, + spans.length, 'all spans have the same transaction_id') + t.equal(spans.filter(s => s.sync === false).length, + spans.length, 'all spans have sync=false') + t.equal(spans.filter(s => s.sample_rate === 1).length, + spans.length, 'all spans have sample_rate=1') + + const failingSpanId = spans[5].id // index of non existing table error + spans.forEach(s => { + // Remove variable and common fields to facilitate t.deepEqual below. + delete s.id + delete s.transaction_id + delete s.parent_id + delete s.trace_id + delete s.timestamp + delete s.duration + delete s.sync + delete s.sample_rate + }) + + // Work through each of the pipeline functions (lisTables, createTable, ...) in the script: + t.deepEqual(spans.shift(), { + name: 'DynamoDB ListTables', + type: 'db', + subtype: 'dynamodb', + action: 'ListTables', + context: { + service: { target: { type: 'dynamodb', name: AWS_REGION } }, + destination: { + address: LOCALSTACK_HOST, + port: 4566, + cloud: { region: 'us-east-2' }, + service: { type: '', name: '', resource: `dynamodb/${AWS_REGION}` } + }, + db: { + instance: AWS_REGION, + type: 'dynamodb' + } + }, + outcome: 'success' + }, 'listTables produced expected span') + + t.deepEqual(spans.shift(), { + name: 'DynamoDB CreateTable elasticapmtest-table-3', + type: 'db', + subtype: 'dynamodb', + action: 'CreateTable', + context: { + service: { target: { type: 'dynamodb', name: AWS_REGION } }, + destination: { + address: LOCALSTACK_HOST, + port: 4566, + cloud: { region: 'us-east-2' }, + service: { type: '', name: '', resource: `dynamodb/${AWS_REGION}` } + }, + db: { + instance: AWS_REGION, + type: 'dynamodb' + } + }, + outcome: 'success' + }, 'createTable produced expected span') + + t.deepEqual(spans.shift(), { + name: 'DynamoDB PutItem elasticapmtest-table-3', + type: 'db', + subtype: 'dynamodb', + action: 'PutItem', + context: { + service: { target: { type: 'dynamodb', name: AWS_REGION } }, + destination: { + address: LOCALSTACK_HOST, + port: 4566, + cloud: { region: 'us-east-2' }, + service: { type: '', name: '', resource: `dynamodb/${AWS_REGION}` } + }, + db: { + instance: AWS_REGION, + type: 'dynamodb' + } + }, + outcome: 'success' + }, 'putItem produced expected span') + + t.deepEqual(spans.shift(), { + name: 'DynamoDB Query elasticapmtest-table-3', + type: 'db', + subtype: 'dynamodb', + action: 'Query', + context: { + service: { target: { type: 'dynamodb', name: AWS_REGION } }, + destination: { + address: LOCALSTACK_HOST, + port: 4566, + cloud: { region: 'us-east-2' }, + service: { type: '', name: '', resource: `dynamodb/${AWS_REGION}` } + }, + db: { + instance: AWS_REGION, + statement: 'RECORD_ID = :foo', + type: 'dynamodb' + } + }, + outcome: 'success' + }, 'query produced expected span') + + t.deepEqual(spans.shift(), { + name: 'get-signed-url', + type: 'custom', + subtype: null, + action: null, + outcome: 'success' + }, 'custom span for getSignedUrl call') + + t.deepEqual(spans.shift(), { + name: 'DynamoDB Query elasticapmtest-table-3-unexistent', + type: 'db', + subtype: 'dynamodb', + action: 'Query', + context: { + service: { target: { type: 'dynamodb', name: AWS_REGION } }, + destination: { + address: LOCALSTACK_HOST, + port: 4566, + cloud: { region: 'us-east-2' }, + service: { type: '', name: '', resource: `dynamodb/${AWS_REGION}` } + }, + db: { + instance: AWS_REGION, + statement: 'RECORD_ID = :foo', + type: 'dynamodb' + } + }, + outcome: 'failure' + }, 'failing query produced expected span') + + t.equal(errors.length, 1, 'got 1 error') + t.equal(errors[0].parent_id, failingSpanId, 'error is a child of the failing span') + t.equal(errors[0].transaction_id, tx.id, 'error.transaction_id') + t.equal(errors[0].exception.type, 'ResourceNotFoundException', 'error.exception.type') + + t.deepEqual(spans.shift(), { + name: 'DynamoDB DeleteItem elasticapmtest-table-3', + type: 'db', + subtype: 'dynamodb', + action: 'DeleteItem', + context: { + service: { target: { type: 'dynamodb', name: AWS_REGION } }, + destination: { + address: LOCALSTACK_HOST, + port: 4566, + cloud: { region: 'us-east-2' }, + service: { type: '', name: '', resource: `dynamodb/${AWS_REGION}` } + }, + db: { + instance: AWS_REGION, + type: 'dynamodb' + } + }, + outcome: 'success' + }, 'deleteItem produced expected span') + + t.deepEqual(spans.shift(), { + name: 'DynamoDB DeleteTable elasticapmtest-table-3', + type: 'db', + subtype: 'dynamodb', + action: 'DeleteTable', + context: { + service: { target: { type: 'dynamodb', name: AWS_REGION } }, + destination: { + address: LOCALSTACK_HOST, + port: 4566, + cloud: { region: 'us-east-2' }, + service: { type: '', name: '', resource: `dynamodb/${AWS_REGION}` } + }, + db: { + instance: AWS_REGION, + type: 'dynamodb' + } + }, + outcome: 'success' + }, 'deleteTable produced expected span') + + t.equal(spans.length, 0, 'all spans accounted for') + } + } +] + +test('@aws-sdk/client-dynamodb fixtures', suite => { + runTestFixtures(suite, testFixtures) + suite.end() +}) diff --git a/test/instrumentation/modules/@aws-sdk/fixtures/use-client-dynamodb.js b/test/instrumentation/modules/@aws-sdk/fixtures/use-client-dynamodb.js new file mode 100644 index 0000000000..7efa677316 --- /dev/null +++ b/test/instrumentation/modules/@aws-sdk/fixtures/use-client-dynamodb.js @@ -0,0 +1,231 @@ +/* + * Copyright Elasticsearch B.V. and other contributors where applicable. + * Licensed under the BSD 2-Clause License; you may not use this file except in + * compliance with the BSD 2-Clause License. + */ + +'use strict' + +// Run a single scenario of using the DynamoDB client (callback style) with APM +// enabled. This is used to test that the expected APM events are generated. +// It writes log.info (in ecs-logging format, see +// https://github.com/trentm/go-ecslog#install) for each S3 client API call. +// +// This script can also be used for manual testing of APM instrumentation of DynamoDB +// against a real DynamoDB account. This can be useful because tests are done against +// https://github.com/localstack/localstack that *simulates* DynamoDB with imperfect +// fidelity. +// +// Auth note: By default this uses the AWS profile/configuration from the +// environment. If you do not have that configured (i.e. do not have +// "~/.aws/...") files, then you can still use localstack via setting: +// unset AWS_PROFILE +// export AWS_ACCESS_KEY_ID=fake +// export AWS_SECRET_ACCESS_KEY=fake +// See also: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html +// +// Usage: +// # Run against the default configured AWS profile, creating a new bucket +// # and deleting it afterwards. +// node use-client-dynamodb.js | ecslog +// +// # Testing against localstack. +// docker run --rm -it -e SERVICES=s3 -p 4566:4566 localstack/localstack +// TEST_ENDPOINT=http://localhost:4566 node use-client-s3.js | ecslog +// +// # Use TEST_TABLE_NAME to re-use an existing table (and not delete it). +// # For safety the table name must start with "elasticapmtest-table-". +// TEST_TABLE_NAME=elasticapmtest-table-3 node use-client-dynamodb.js | ecslog +// +// Output from a sample run is here: +// https://gist.github.com/david-luna/2b785a3197891505902fa85ee8ff3e3d + +const apm = require('../../../../..').start({ + serviceName: 'use-client-dyamodb', + captureExceptions: false, + centralConfig: false, + metricsInterval: 0, + cloudProvider: 'none', + stackTraceLimit: 4, // get it smaller for reviewing output + logLevel: 'info' +}) + +// const crypto = require('crypto') +const assert = require('assert') +const { + DynamoDBClient, + ListTablesCommand, + CreateTableCommand, + PutItemCommand, + QueryCommand, + DeleteItemCommand, + DeleteTableCommand +} = require('@aws-sdk/client-dynamodb') +const { getSignedUrl } = require('@aws-sdk/s3-request-presigner') + +const TEST_TABLE_NAME_PREFIX = 'elasticapmtest-table-' + +// https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/ +async function useClientDynamoDB (dynamoDBClient, tableName) { + const region = await dynamoDBClient.config.region() + const log = apm.logger.child({ + 'event.module': 'app', + endpoint: dynamoDBClient.config.endpoint, + region + }) + + let command + let data + + command = new ListTablesCommand() + data = await dynamoDBClient.send(command) + assert(apm.currentSpan === null, + 'DynamoDB span (or its HTTP span) should not be currentSpan after awaiting the task') + log.info({ data }, 'query') + + const tableIsPreexisting = data.TableNames.some(t => t === tableName) + // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/CreateTableCommand/ + if (!tableIsPreexisting) { + command = new CreateTableCommand({ + TableName: tableName, + AttributeDefinitions: [ + { + AttributeName: 'RECORD_ID', + AttributeType: 'S' + } + ], + KeySchema: [ + { + AttributeName: 'RECORD_ID', + KeyType: 'HASH' + } + ], + ProvisionedThroughput: { + ReadCapacityUnits: 1, + WriteCapacityUnits: 1 + } + }) + data = await dynamoDBClient.send(command) + log.info({ data }, 'createTable') + } + + // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/PutItemCommand/ + command = new PutItemCommand({ + TableName: tableName, + Item: { + RECORD_ID: { S: '001' } + } + }) + data = await dynamoDBClient.send(command) + assert(apm.currentSpan === null, + 'DynamoDB span (or its HTTP span) should not be currentSpan after awaiting the task') + log.info({ data }, 'putItem') + + // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/QueryCommand/ + command = new QueryCommand({ + TableName: tableName, + KeyConditionExpression: 'RECORD_ID = :foo', + ExpressionAttributeValues: { + ':foo': { S: '001' } + } + }) + data = await dynamoDBClient.send(command) + assert(apm.currentSpan === null, + 'DynamoDB span (or its HTTP span) should not be currentSpan after awaiting the task') + log.info({ data }, 'query') + + // Get a signed URL. + // This is interesting to test, because `getSignedUrl` uses the command + // `middlewareStack` -- including our added middleware -- **without** calling + // `dynamoDBClient.send()`. The test here is to ensure this doesn't break. + const customSpan = apm.startSpan('get-signed-url') + const signedUrl = await getSignedUrl( + dynamoDBClient, + new QueryCommand({ + TableName: tableName, + KeyConditionExpression: 'RECORD_ID = :foo', + ExpressionAttributeValues: { ':foo': { S: '001' } } + }), + { expiresIn: 3600 }) + log.info({ signedUrl }, 'getSignedUrl') + customSpan.end() + + command = new QueryCommand({ + TableName: tableName + '-unexistent', + KeyConditionExpression: 'RECORD_ID = :foo', + ExpressionAttributeValues: { + ':foo': { S: '001' } + } + }) + try { + data = await dynamoDBClient.send(command) + throw new Error('expected ResourceNotFoundException error for query') + } catch (err) { + log.info({ err }, 'query with error') + const statusCode = err && err.$metadata && err.$metadata.httpStatusCode + if (statusCode !== 400) { + throw err + } + } + + // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/DeleteItemCommand/ + command = new DeleteItemCommand({ + TableName: tableName, + Key: { + RECORD_ID: { S: '001' } + } + }) + data = await dynamoDBClient.send(command) + log.info({ data }, 'deleteItem') + + // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/dynamodb/command/DeleteTableCommand/ + command = new DeleteTableCommand({ TableName: tableName }) + data = await dynamoDBClient.send(command) + log.info({ data }, 'deleteTable') +} + +// Return a timestamp of the form YYYYMMDDHHMMSS, which can be used in an S3 +// bucket name: +// https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.NamingRules +function getTimestamp () { + return (new Date()).toISOString().split('.')[0].replace(/[^0-9]/g, '') +} + +// ---- mainline + +function main () { + const region = process.env.TEST_REGION || 'us-east-2' + const endpoint = process.env.TEST_ENDPOINT || null + const tableName = process.env.TEST_TABLE_NAME || TEST_TABLE_NAME_PREFIX + getTimestamp() + + // Guard against any table name being used because we will be creating and + // deleting records in it, and potentially *deleting* the table. + if (!tableName.startsWith(TEST_TABLE_NAME_PREFIX)) { + throw new Error(`cannot use table name "${tableName}", it must start with ${TEST_TABLE_NAME_PREFIX}`) + } + + const dynamoDBClient = new DynamoDBClient({ + region, + endpoint + }) + + // Ensure an APM transaction so spans can happen. + const tx = apm.startTransaction('manual') + + useClientDynamoDB(dynamoDBClient, tableName).then( + function () { + tx.end() + dynamoDBClient.destroy() + process.exitCode = 0 + }, + function (err) { + apm.logger.error(err, 'useClientDynamoDB rejected') + tx.setOutcome('failure') + tx.end() + dynamoDBClient.destroy() + process.exitCode = 1 + } + ) +} + +main() diff --git a/test/instrumentation/modules/@aws-sdk/fixtures/use-client-s3.js b/test/instrumentation/modules/@aws-sdk/fixtures/use-client-s3.js index 6ccc319d0b..63b136bcfe 100644 --- a/test/instrumentation/modules/@aws-sdk/fixtures/use-client-s3.js +++ b/test/instrumentation/modules/@aws-sdk/fixtures/use-client-s3.js @@ -65,31 +65,9 @@ const { } = require('@aws-sdk/client-s3') const { getSignedUrl } = require('@aws-sdk/s3-request-presigner') -const TEST_BUCKET_NAME_PREFIX = 'elasticapmtest-bucket-' - -// ---- support functions +const { slurpStream } = require('../../../../_utils') -/** - * Slurp everything from the given ReadableStream and return the content, - * converted to a string. - */ -async function slurpStream (stream) { - return new Promise((resolve, reject) => { - const chunks = [] - stream.on('error', (err) => { - reject(err) - }) - stream.on('readable', function () { - let chunk - while ((chunk = this.read()) !== null) { - chunks.push(chunk) - } - }) - stream.on('end', () => { - resolve(Buffer.concat(chunks).toString('utf8')) - }) - }) -} +const TEST_BUCKET_NAME_PREFIX = 'elasticapmtest-bucket-' // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/index.html async function useClientS3 (s3Client, bucketName) {