From cb311053b9c2f0d2925639217fdac7225a390d41 Mon Sep 17 00:00:00 2001
From: Michael He <53622546+yiyuan-he@users.noreply.github.com>
Date: Tue, 8 Oct 2024 17:34:07 -0700
Subject: [PATCH] =?UTF-8?q?feat:=20Add=20auto-instrumentation=20support=20?=
=?UTF-8?q?for=20Step=20Functions,=20SNS,=20Secre=E2=80=A6=20(#899)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
### *Description of changes:*
Changes in ADOT package to support new AWS resources in Java V1 & V2
SDKs.
Related changes for V1 SDK in upstream package:
https://github.com/mxiamxia/opentelemetry-java-instrumentation/pull/11
Related changes for V2 SDK in upstream package:
https://github.com/mxiamxia/opentelemetry-java-instrumentation/pull/12
These changes add auto-instrumentation support for the following AWS
resources. Additionally, a new attribute for
`aws.remote.resource.cfn.primary.identifier` is also added for each new
resource.
- Populate `aws.sns.topic.arn` in Span by extracting `TopicArn` from the
request body.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sns-topic.html
- The CFN Id should be the ARN of the Topic.
- Populate `aws.secretsmanager.secret.arn` in Span by extracting `ARN`
from response body.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-secretsmanager-secret.html
- The CFN Id should be the ARN of the Secret.
- Populate `aws.stepfunctions.state_machine.arn` in Span by extracting
`stateMachineArn` from the request body.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-statemachine.html
- The CFN Id should be the ARN of the State Machine.
- Populate `aws.stepfunctions.activity.arn` in Span by extracting
`activityArn` from the request body.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-stepfunctions-activity.html
- The CFN Id should be the ARN of the Activity.
- Populate `aws.lambda.function.name` in Span by extracting
`FunctionName` from the request body.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html
- The CFN Id should be the ARN of the Lambda Function.
- Populate `aws.lambda.resource_mapping.id` in Span by extracting `UUID`
from the request body.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html
- The CFN Id should be the UUID of the Event Source Mapping.
### *Test Plan:*
Set up a client-server with auto-instrumentation to verify that the
correct span data is being generated.
Note: V1 span data is top pic and V2 span data is bottom pic for each
resource.
`aws.lambda.function.name`
`aws.lambda.resource_mapping.id`
`aws.secretsmanager.secret.arn`
`aws.sns.topic.arn`
`aws.stepfunctions.activity.arn`
`aws.stepfunction.state_machine.arn`
Metric Attribute Generator Unit Test
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
---
awsagentprovider/build.gradle.kts | 2 +
.../javaagent/providers/AwsAttributeKeys.java | 23 +++++
.../AwsMetricAttributeGenerator.java | 95 +++++++++++++++++++
.../AwsMetricAttributeGeneratorTest.java | 43 +++++++++
4 files changed, 163 insertions(+)
diff --git a/awsagentprovider/build.gradle.kts b/awsagentprovider/build.gradle.kts
index 5b99011ab7..3e92ad8b1a 100644
--- a/awsagentprovider/build.gradle.kts
+++ b/awsagentprovider/build.gradle.kts
@@ -36,6 +36,8 @@ dependencies {
implementation("io.opentelemetry.contrib:opentelemetry-aws-resources")
// Json file reader
implementation("com.fasterxml.jackson.core:jackson-databind:2.16.1")
+ // Import AWS SDK v1 core for ARN parsing utilities
+ implementation("com.amazonaws:aws-java-sdk-core:1.12.773")
// Export configuration
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp")
diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java
index 457e81d77d..ef2999a28a 100644
--- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java
+++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java
@@ -38,6 +38,9 @@ private AwsAttributeKeys() {}
static final AttributeKey AWS_REMOTE_RESOURCE_IDENTIFIER =
AttributeKey.stringKey("aws.remote.resource.identifier");
+ static final AttributeKey AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER =
+ AttributeKey.stringKey("aws.remote.resource.cfn.primary.identifier");
+
static final AttributeKey AWS_REMOTE_RESOURCE_TYPE =
AttributeKey.stringKey("aws.remote.resource.type");
@@ -50,6 +53,26 @@ private AwsAttributeKeys() {}
static final AttributeKey AWS_CONSUMER_PARENT_SPAN_KIND =
AttributeKey.stringKey("aws.consumer.parent.span.kind");
+ static final AttributeKey AWS_STATE_MACHINE_ARN =
+ AttributeKey.stringKey("aws.stepfunctions.state_machine.arn");
+
+ static final AttributeKey AWS_STEP_FUNCTIONS_ACTIVITY_ARN =
+ AttributeKey.stringKey("aws.stepfunctions.activity.arn");
+
+ static final AttributeKey AWS_SNS_TOPIC_ARN = AttributeKey.stringKey("aws.sns.topic.arn");
+
+ static final AttributeKey AWS_SECRET_ARN =
+ AttributeKey.stringKey("aws.secretsmanager.secret.arn");
+
+ static final AttributeKey AWS_LAMBDA_NAME =
+ AttributeKey.stringKey("aws.lambda.function.name");
+
+ static final AttributeKey AWS_LAMBDA_ARN =
+ AttributeKey.stringKey("aws.lambda.function.arn");
+
+ static final AttributeKey AWS_LAMBDA_RESOURCE_ID =
+ AttributeKey.stringKey("aws.lambda.resource_mapping.id");
+
// use the same AWS Resource attribute name defined by OTel java auto-instr for aws_sdk_v_1_1
// TODO: all AWS specific attributes should be defined in semconv package and reused cross all
// otel packages. Related sim -
diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java
index 68ba1bc824..63c806ea98 100644
--- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java
+++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java
@@ -42,9 +42,12 @@
import static io.opentelemetry.semconv.SemanticAttributes.SERVER_SOCKET_PORT;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_AGENT_ID;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_BUCKET_NAME;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_DATA_SOURCE_ID;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_GUARDRAIL_ID;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_KNOWLEDGE_BASE_ID;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_NAME;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_RESOURCE_ID;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_OPERATION;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_SERVICE;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_NAME;
@@ -54,7 +57,11 @@
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_IDENTIFIER;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_TYPE;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_SERVICE;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_SECRET_ARN;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_SNS_TOPIC_ARN;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_SPAN_KIND;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_STATE_MACHINE_ARN;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_STEP_FUNCTIONS_ACTIVITY_ARN;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_STREAM_NAME;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_TABLE_NAME;
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL;
@@ -67,6 +74,7 @@
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isDBSpan;
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isKeyPresent;
+import com.amazonaws.arn.Arn;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
@@ -111,6 +119,10 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
private static final String NORMALIZED_SQS_SERVICE_NAME = "AWS::SQS";
private static final String NORMALIZED_BEDROCK_SERVICE_NAME = "AWS::Bedrock";
private static final String NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME = "AWS::BedrockRuntime";
+ private static final String NORMALIZED_STEPFUNCTIONS_SERVICE_NAME = "AWS::StepFunctions";
+ private static final String NORMALIZED_SNS_SERVICE_NAME = "AWS::SNS";
+ private static final String NORMALIZED_SECRETSMANAGER_SERVICE_NAME = "AWS::SecretsManager";
+ private static final String NORMALIZED_LAMBDA_SERVICE_NAME = "AWS::Lambda";
// Special DEPENDENCY attribute value if GRAPHQL_OPERATION_TYPE attribute key is present.
private static final String GRAPHQL = "graphql";
@@ -371,6 +383,18 @@ private static String normalizeRemoteServiceName(SpanData span, String serviceNa
case "AmazonBedrockRuntime": // AWS SDK v1
case "BedrockRuntime": // AWS SDK v2
return NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME;
+ case "AWSStepFunctions": // AWS SDK v1
+ case "Sfn": // AWS SDK v2
+ return NORMALIZED_STEPFUNCTIONS_SERVICE_NAME;
+ case "AmazonSNS":
+ case "Sns":
+ return NORMALIZED_SNS_SERVICE_NAME;
+ case "AWSSecretsManager": // AWS SDK v1
+ case "SecretsManager": // AWS SDK v2
+ return NORMALIZED_SECRETSMANAGER_SERVICE_NAME;
+ case "AWSLambda": // AWS SDK v1
+ case "Lambda": // AWS SDK v2
+ return NORMALIZED_LAMBDA_SERVICE_NAME;
default:
return "AWS::" + serviceName;
}
@@ -392,6 +416,7 @@ private static String normalizeRemoteServiceName(SpanData span, String serviceNa
private static void setRemoteResourceTypeAndIdentifier(SpanData span, AttributesBuilder builder) {
Optional remoteResourceType = Optional.empty();
Optional remoteResourceIdentifier = Optional.empty();
+ Optional cloudformationPrimaryIdentifier = Optional.empty();
if (isAwsSDKSpan(span)) {
if (isKeyPresent(span, AWS_TABLE_NAME)) {
@@ -434,18 +459,88 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes
remoteResourceType = Optional.of(NORMALIZED_BEDROCK_SERVICE_NAME + "::Model");
remoteResourceIdentifier =
Optional.ofNullable(escapeDelimiters(span.getAttributes().get(GEN_AI_REQUEST_MODEL)));
+ } else if (isKeyPresent(span, AWS_STATE_MACHINE_ARN)) {
+ remoteResourceType = Optional.of(NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + "::StateMachine");
+ remoteResourceIdentifier =
+ getSfnResourceNameFromArn(
+ Optional.ofNullable(
+ escapeDelimiters(span.getAttributes().get(AWS_STATE_MACHINE_ARN))));
+ cloudformationPrimaryIdentifier =
+ Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_STATE_MACHINE_ARN)));
+ } else if (isKeyPresent(span, AWS_STEP_FUNCTIONS_ACTIVITY_ARN)) {
+ remoteResourceType = Optional.of(NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + "::Activity");
+ remoteResourceIdentifier =
+ getSfnResourceNameFromArn(
+ Optional.ofNullable(
+ escapeDelimiters(span.getAttributes().get(AWS_STEP_FUNCTIONS_ACTIVITY_ARN))));
+ cloudformationPrimaryIdentifier =
+ Optional.ofNullable(
+ escapeDelimiters(span.getAttributes().get(AWS_STEP_FUNCTIONS_ACTIVITY_ARN)));
+ } else if (isKeyPresent(span, AWS_SNS_TOPIC_ARN)) {
+ remoteResourceType = Optional.of(NORMALIZED_SNS_SERVICE_NAME + "::Topic");
+ remoteResourceIdentifier =
+ getSnsResourceNameFromArn(
+ Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_SNS_TOPIC_ARN))));
+ cloudformationPrimaryIdentifier =
+ Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_SNS_TOPIC_ARN)));
+ } else if (isKeyPresent(span, AWS_SECRET_ARN)) {
+ remoteResourceType = Optional.of(NORMALIZED_SECRETSMANAGER_SERVICE_NAME + "::Secret");
+ remoteResourceIdentifier =
+ getSecretsManagerResourceNameFromArn(
+ Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_SECRET_ARN))));
+ cloudformationPrimaryIdentifier =
+ Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_SECRET_ARN)));
+ } else if (isKeyPresent(span, AWS_LAMBDA_NAME)) {
+ remoteResourceType = Optional.of(NORMALIZED_LAMBDA_SERVICE_NAME + "::Function");
+ remoteResourceIdentifier =
+ getLambdaResourceNameFromAribitraryName(
+ Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_LAMBDA_NAME))));
+ } else if (isKeyPresent(span, AWS_LAMBDA_RESOURCE_ID)) {
+ remoteResourceType = Optional.of(NORMALIZED_LAMBDA_SERVICE_NAME + "::EventSourceMapping");
+ remoteResourceIdentifier =
+ Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_LAMBDA_RESOURCE_ID)));
}
} else if (isDBSpan(span)) {
remoteResourceType = Optional.of(DB_CONNECTION_RESOURCE_TYPE);
remoteResourceIdentifier = getDbConnection(span);
}
+ if (cloudformationPrimaryIdentifier.isEmpty()) {
+ cloudformationPrimaryIdentifier = remoteResourceIdentifier;
+ }
+
if (remoteResourceType.isPresent() && remoteResourceIdentifier.isPresent()) {
builder.put(AWS_REMOTE_RESOURCE_TYPE, remoteResourceType.get());
builder.put(AWS_REMOTE_RESOURCE_IDENTIFIER, remoteResourceIdentifier.get());
+ builder.put(AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER, cloudformationPrimaryIdentifier.get());
}
}
+ // NOTE: "name" in this case can be either the lambda name or lambda arn
+ private static Optional getLambdaResourceNameFromAribitraryName(
+ Optional arbitraryName) {
+ if (arbitraryName != null && arbitraryName.get().startsWith("arn:aws:lambda:")) {
+ Arn resourceArn = Arn.fromString(arbitraryName.get());
+ return Optional.of(resourceArn.getResource().toString().split(":")[1]);
+ }
+ return arbitraryName;
+ }
+
+ private static Optional getSecretsManagerResourceNameFromArn(Optional stringArn) {
+ Arn resourceArn = Arn.fromString(stringArn.get());
+ return Optional.of(resourceArn.getResource().toString().split(":")[1]);
+ }
+
+ private static Optional getSfnResourceNameFromArn(Optional stringArn) {
+ Arn resourceArn = Arn.fromString(stringArn.get());
+ return Optional.of(resourceArn.getResource().toString().split(":")[1]);
+ }
+
+ private static Optional getSnsResourceNameFromArn(Optional stringArn) {
+ Arn resourceArn = Arn.fromString(stringArn.get());
+ return Optional.of(resourceArn.getResource().toString());
+ }
+
/**
* RemoteResourceIdentifier is populated with rule
* ^[{db.name}|]?{address}[|{port}]?
diff --git a/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java b/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java
index 594d3f7dc9..c51188a6e8 100644
--- a/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java
+++ b/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java
@@ -26,6 +26,8 @@
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_DATA_SOURCE_ID;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_GUARDRAIL_ID;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_KNOWLEDGE_BASE_ID;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_NAME;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_RESOURCE_ID;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_OPERATION;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_SERVICE;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_NAME;
@@ -35,7 +37,11 @@
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_IDENTIFIER;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_TYPE;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_SERVICE;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_SECRET_ARN;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_SNS_TOPIC_ARN;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_SPAN_KIND;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_STATE_MACHINE_ARN;
+import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_STEP_FUNCTIONS_ACTIVITY_ARN;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_STREAM_NAME;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_TABLE_NAME;
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL;
@@ -770,6 +776,42 @@ public void testSdkClientSpanWithRemoteResourceAttributes() {
mockAttribute(GEN_AI_REQUEST_MODEL, "test.service_^id");
validateRemoteResourceAttributes("AWS::Bedrock::Model", "test.service_^^id");
mockAttribute(GEN_AI_REQUEST_MODEL, null);
+
+ // Validate behaviour of AWS_STATE_MACHINE_ARN attribute, then remove it.
+ mockAttribute(
+ AWS_STATE_MACHINE_ARN,
+ "arn:aws:states:us-east-1:123456789012:stateMachine:test_state_machine");
+ validateRemoteResourceAttributes("AWS::StepFunctions::StateMachine", "test_state_machine");
+ mockAttribute(AWS_STATE_MACHINE_ARN, null);
+
+ // Validate behaviour of AWS_STEPFUNCTIONS_ACTIVITY_ARN, then remove it.
+ mockAttribute(
+ AWS_STEP_FUNCTIONS_ACTIVITY_ARN,
+ "arn:aws:states:us-east-1:007003123456789012:activity:testActivity");
+ validateRemoteResourceAttributes("AWS::StepFunctions::Activity", "testActivity");
+ mockAttribute(AWS_STEP_FUNCTIONS_ACTIVITY_ARN, null);
+
+ // Validate behaviour of AWS_SNS_TOPIC_ARN, then remove it.
+ mockAttribute(AWS_SNS_TOPIC_ARN, "arn:aws:sns:us-west-2:012345678901:testTopic");
+ validateRemoteResourceAttributes("AWS::SNS::Topic", "testTopic");
+ mockAttribute(AWS_SNS_TOPIC_ARN, null);
+
+ // Validate behaviour of AWS_SECRET_ARN, then remove it.
+ mockAttribute(
+ AWS_SECRET_ARN, "arn:aws:secretsmanager:us-east-1:123456789012:secret:secretName");
+ validateRemoteResourceAttributes("AWS::SecretsManager::Secret", "secretName");
+ mockAttribute(AWS_SECRET_ARN, null);
+
+ // Validate behaviour of AWS_LAMBDA_NAME, then remove it.
+ mockAttribute(AWS_LAMBDA_NAME, "arn:aws:lambda:us-east-1:123456789012:function:functionName");
+ validateRemoteResourceAttributes("AWS::Lambda::Function", "functionName");
+ mockAttribute(AWS_LAMBDA_NAME, null);
+
+ // Validate behaviour of AWS_LAMBDA_RESOURCE_ID
+ mockAttribute(AWS_LAMBDA_RESOURCE_ID, "eventSourceId");
+ validateRemoteResourceAttributes("AWS::Lambda::EventSourceMapping", "eventSourceId");
+ mockAttribute(AWS_LAMBDA_RESOURCE_ID, null);
+
mockAttribute(RPC_SYSTEM, "null");
}
@@ -1175,6 +1217,7 @@ public void testNormalizeRemoteServiceName_AwsSdk() {
testAwsSdkServiceNormalization("AWSBedrockAgentRuntime", "AWS::Bedrock");
testAwsSdkServiceNormalization("AWSBedrockAgent", "AWS::Bedrock");
testAwsSdkServiceNormalization("AmazonBedrockRuntime", "AWS::BedrockRuntime");
+ testAwsSdkServiceNormalization("AWSStepFunctions", "AWS::StepFunctions");
// AWS SDK V2
testAwsSdkServiceNormalization("DynamoDb", "AWS::DynamoDB");