1010
1111from amazon .base .contract_test_base import NETWORK_NAME , ContractTestBase
1212from amazon .utils .application_signals_constants import (
13+ AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER ,
1314 AWS_LOCAL_OPERATION ,
1415 AWS_LOCAL_SERVICE ,
1516 AWS_REMOTE_OPERATION ,
@@ -99,6 +100,7 @@ def test_s3_create_bucket(self):
99100 remote_operation = "CreateBucket" ,
100101 remote_resource_type = "AWS::S3::Bucket" ,
101102 remote_resource_identifier = "test-bucket-name" ,
103+ cloudformation_primary_identifier = "test-bucket-name" ,
102104 request_specific_attributes = {
103105 SpanAttributes .AWS_S3_BUCKET : "test-bucket-name" ,
104106 },
@@ -116,6 +118,7 @@ def test_s3_create_object(self):
116118 remote_operation = "PutObject" ,
117119 remote_resource_type = "AWS::S3::Bucket" ,
118120 remote_resource_identifier = "test-put-object-bucket-name" ,
121+ cloudformation_primary_identifier = "test-put-object-bucket-name" ,
119122 request_specific_attributes = {
120123 SpanAttributes .AWS_S3_BUCKET : "test-put-object-bucket-name" ,
121124 },
@@ -133,6 +136,7 @@ def test_s3_get_object(self):
133136 remote_operation = "GetObject" ,
134137 remote_resource_type = "AWS::S3::Bucket" ,
135138 remote_resource_identifier = "test-get-object-bucket-name" ,
139+ cloudformation_primary_identifier = "test-get-object-bucket-name" ,
136140 request_specific_attributes = {
137141 SpanAttributes .AWS_S3_BUCKET : "test-get-object-bucket-name" ,
138142 },
@@ -150,6 +154,7 @@ def test_s3_error(self):
150154 remote_operation = "CreateBucket" ,
151155 remote_resource_type = "AWS::S3::Bucket" ,
152156 remote_resource_identifier = "-" ,
157+ cloudformation_primary_identifier = "-" ,
153158 request_specific_attributes = {
154159 SpanAttributes .AWS_S3_BUCKET : "-" ,
155160 },
@@ -167,6 +172,7 @@ def test_s3_fault(self):
167172 remote_operation = "CreateBucket" ,
168173 remote_resource_type = "AWS::S3::Bucket" ,
169174 remote_resource_identifier = "valid-bucket-name" ,
175+ cloudformation_primary_identifier = "valid-bucket-name" ,
170176 request_specific_attributes = {
171177 SpanAttributes .AWS_S3_BUCKET : "valid-bucket-name" ,
172178 },
@@ -184,6 +190,7 @@ def test_dynamodb_create_table(self):
184190 remote_operation = "CreateTable" ,
185191 remote_resource_type = "AWS::DynamoDB::Table" ,
186192 remote_resource_identifier = "test_table" ,
193+ cloudformation_primary_identifier = "test_table" ,
187194 request_specific_attributes = {
188195 SpanAttributes .AWS_DYNAMODB_TABLE_NAMES : ["test_table" ],
189196 },
@@ -201,6 +208,7 @@ def test_dynamodb_put_item(self):
201208 remote_operation = "PutItem" ,
202209 remote_resource_type = "AWS::DynamoDB::Table" ,
203210 remote_resource_identifier = "put_test_table" ,
211+ cloudformation_primary_identifier = "put_test_table" ,
204212 request_specific_attributes = {
205213 SpanAttributes .AWS_DYNAMODB_TABLE_NAMES : ["put_test_table" ],
206214 },
@@ -218,6 +226,7 @@ def test_dynamodb_error(self):
218226 remote_operation = "PutItem" ,
219227 remote_resource_type = "AWS::DynamoDB::Table" ,
220228 remote_resource_identifier = "invalid_table" ,
229+ cloudformation_primary_identifier = "invalid_table" ,
221230 request_specific_attributes = {
222231 SpanAttributes .AWS_DYNAMODB_TABLE_NAMES : ["invalid_table" ],
223232 },
@@ -235,6 +244,7 @@ def test_dynamodb_fault(self):
235244 remote_operation = "PutItem" ,
236245 remote_resource_type = "AWS::DynamoDB::Table" ,
237246 remote_resource_identifier = "invalid_table" ,
247+ cloudformation_primary_identifier = "invalid_table" ,
238248 request_specific_attributes = {
239249 SpanAttributes .AWS_DYNAMODB_TABLE_NAMES : ["invalid_table" ],
240250 },
@@ -252,9 +262,13 @@ def test_sqs_create_queue(self):
252262 remote_operation = "CreateQueue" ,
253263 remote_resource_type = "AWS::SQS::Queue" ,
254264 remote_resource_identifier = "test_queue" ,
265+ cloudformation_primary_identifier = "http://sqs.us-west-2.localhost.localstack.cloud:4566/000000000000/test_queue" ,
255266 request_specific_attributes = {
256267 _AWS_SQS_QUEUE_NAME : "test_queue" ,
257268 },
269+ response_specific_attributes = {
270+ _AWS_SQS_QUEUE_URL : "http://sqs.us-west-2.localhost.localstack.cloud:4566/000000000000/test_queue" ,
271+ },
258272 span_name = "SQS.CreateQueue" ,
259273 )
260274
@@ -269,6 +283,7 @@ def test_sqs_send_message(self):
269283 remote_operation = "SendMessage" ,
270284 remote_resource_type = "AWS::SQS::Queue" ,
271285 remote_resource_identifier = "test_put_get_queue" ,
286+ cloudformation_primary_identifier = "http://localstack:4566/000000000000/test_put_get_queue" ,
272287 request_specific_attributes = {
273288 _AWS_SQS_QUEUE_URL : "http://localstack:4566/000000000000/test_put_get_queue" ,
274289 },
@@ -286,6 +301,7 @@ def test_sqs_receive_message(self):
286301 remote_operation = "ReceiveMessage" ,
287302 remote_resource_type = "AWS::SQS::Queue" ,
288303 remote_resource_identifier = "test_put_get_queue" ,
304+ cloudformation_primary_identifier = "http://localstack:4566/000000000000/test_put_get_queue" ,
289305 request_specific_attributes = {
290306 _AWS_SQS_QUEUE_URL : "http://localstack:4566/000000000000/test_put_get_queue" ,
291307 },
@@ -303,6 +319,7 @@ def test_sqs_error(self):
303319 remote_operation = "SendMessage" ,
304320 remote_resource_type = "AWS::SQS::Queue" ,
305321 remote_resource_identifier = "sqserror" ,
322+ cloudformation_primary_identifier = "http://error.test:8080/000000000000/sqserror" ,
306323 request_specific_attributes = {
307324 _AWS_SQS_QUEUE_URL : "http://error.test:8080/000000000000/sqserror" ,
308325 },
@@ -320,6 +337,7 @@ def test_sqs_fault(self):
320337 remote_operation = "CreateQueue" ,
321338 remote_resource_type = "AWS::SQS::Queue" ,
322339 remote_resource_identifier = "invalid_test" ,
340+ cloudformation_primary_identifier = "invalid_test" ,
323341 request_specific_attributes = {
324342 _AWS_SQS_QUEUE_NAME : "invalid_test" ,
325343 },
@@ -337,6 +355,7 @@ def test_kinesis_put_record(self):
337355 remote_operation = "PutRecord" ,
338356 remote_resource_type = "AWS::Kinesis::Stream" ,
339357 remote_resource_identifier = "test_stream" ,
358+ cloudformation_primary_identifier = "test_stream" ,
340359 request_specific_attributes = {
341360 _AWS_KINESIS_STREAM_NAME : "test_stream" ,
342361 },
@@ -354,6 +373,7 @@ def test_kinesis_error(self):
354373 remote_operation = "PutRecord" ,
355374 remote_resource_type = "AWS::Kinesis::Stream" ,
356375 remote_resource_identifier = "invalid_stream" ,
376+ cloudformation_primary_identifier = "invalid_stream" ,
357377 request_specific_attributes = {
358378 _AWS_KINESIS_STREAM_NAME : "invalid_stream" ,
359379 },
@@ -371,6 +391,7 @@ def test_kinesis_fault(self):
371391 remote_operation = "PutRecord" ,
372392 remote_resource_type = "AWS::Kinesis::Stream" ,
373393 remote_resource_identifier = "test_stream" ,
394+ cloudformation_primary_identifier = "test_stream" ,
374395 request_specific_attributes = {
375396 _AWS_KINESIS_STREAM_NAME : "test_stream" ,
376397 },
@@ -389,6 +410,7 @@ def test_bedrock_runtime_invoke_model(self):
389410 remote_operation = "InvokeModel" ,
390411 remote_resource_type = "AWS::Bedrock::Model" ,
391412 remote_resource_identifier = "amazon.titan-text-premier-v1:0" ,
413+ cloudformation_primary_identifier = "amazon.titan-text-premier-v1:0" ,
392414 request_specific_attributes = {
393415 _GEN_AI_REQUEST_MODEL : "amazon.titan-text-premier-v1:0" ,
394416 },
@@ -407,6 +429,7 @@ def test_bedrock_get_guardrail(self):
407429 remote_operation = "GetGuardrail" ,
408430 remote_resource_type = "AWS::Bedrock::Guardrail" ,
409431 remote_resource_identifier = "bt4o77i015cu" ,
432+ cloudformation_primary_identifier = "arn:aws:bedrock:us-east-1:000000000000:guardrail/bt4o77i015cu" ,
410433 request_specific_attributes = {
411434 _AWS_BEDROCK_GUARDRAIL_ID : "bt4o77i015cu" ,
412435 },
@@ -425,6 +448,7 @@ def test_bedrock_agent_runtime_invoke_agent(self):
425448 remote_operation = "InvokeAgent" ,
426449 remote_resource_type = "AWS::Bedrock::Agent" ,
427450 remote_resource_identifier = "Q08WFRPHVL" ,
451+ cloudformation_primary_identifier = "Q08WFRPHVL" ,
428452 request_specific_attributes = {
429453 _AWS_BEDROCK_AGENT_ID : "Q08WFRPHVL" ,
430454 },
@@ -443,6 +467,7 @@ def test_bedrock_agent_runtime_retrieve(self):
443467 remote_operation = "Retrieve" ,
444468 remote_resource_type = "AWS::Bedrock::KnowledgeBase" ,
445469 remote_resource_identifier = "test-knowledge-base-id" ,
470+ cloudformation_primary_identifier = "test-knowledge-base-id" ,
446471 request_specific_attributes = {
447472 _AWS_BEDROCK_KNOWLEDGE_BASE_ID : "test-knowledge-base-id" ,
448473 },
@@ -461,6 +486,7 @@ def test_bedrock_agent_get_agent(self):
461486 remote_operation = "GetAgent" ,
462487 remote_resource_type = "AWS::Bedrock::Agent" ,
463488 remote_resource_identifier = "TESTAGENTID" ,
489+ cloudformation_primary_identifier = "TESTAGENTID" ,
464490 request_specific_attributes = {
465491 _AWS_BEDROCK_AGENT_ID : "TESTAGENTID" ,
466492 },
@@ -479,6 +505,7 @@ def test_bedrock_agent_get_knowledge_base(self):
479505 remote_operation = "GetKnowledgeBase" ,
480506 remote_resource_type = "AWS::Bedrock::KnowledgeBase" ,
481507 remote_resource_identifier = "invalid-knowledge-base-id" ,
508+ cloudformation_primary_identifier = "invalid-knowledge-base-id" ,
482509 request_specific_attributes = {
483510 _AWS_BEDROCK_KNOWLEDGE_BASE_ID : "invalid-knowledge-base-id" ,
484511 },
@@ -497,8 +524,10 @@ def test_bedrock_agent_get_data_source(self):
497524 remote_operation = "GetDataSource" ,
498525 remote_resource_type = "AWS::Bedrock::DataSource" ,
499526 remote_resource_identifier = "DATASURCID" ,
527+ cloudformation_primary_identifier = "TESTKBSEID|DATASURCID" ,
500528 request_specific_attributes = {
501529 _AWS_BEDROCK_DATA_SOURCE_ID : "DATASURCID" ,
530+ _AWS_BEDROCK_KNOWLEDGE_BASE_ID : "TESTKBSEID" ,
502531 },
503532 span_name = "Bedrock Agent.GetDataSource" ,
504533 )
@@ -519,6 +548,7 @@ def _assert_aws_span_attributes(self, resource_scope_spans: List[ResourceScopeSp
519548 "LOCAL_ROOT" ,
520549 kwargs .get ("remote_resource_type" , "None" ),
521550 kwargs .get ("remote_resource_identifier" , "None" ),
551+ kwargs .get ("cloudformation_primary_identifier" , "None" ),
522552 )
523553
524554 def _assert_aws_attributes (
@@ -529,6 +559,7 @@ def _assert_aws_attributes(
529559 span_kind : str ,
530560 remote_resource_type : str ,
531561 remote_resource_identifier : str ,
562+ cloudformation_primary_identifier : str ,
532563 ) -> None :
533564 attributes_dict : Dict [str , AnyValue ] = self ._get_attributes_dict (attributes_list )
534565 self ._assert_str_attribute (attributes_dict , AWS_LOCAL_SERVICE , self .get_application_otel_service_name ())
@@ -541,6 +572,8 @@ def _assert_aws_attributes(
541572 self ._assert_str_attribute (attributes_dict , AWS_REMOTE_RESOURCE_TYPE , remote_resource_type )
542573 if remote_resource_identifier != "None" :
543574 self ._assert_str_attribute (attributes_dict , AWS_REMOTE_RESOURCE_IDENTIFIER , remote_resource_identifier )
575+ if cloudformation_primary_identifier != "None" :
576+ self ._assert_str_attribute (attributes_dict , AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER , cloudformation_primary_identifier )
544577 # See comment above AWS_LOCAL_OPERATION
545578 self ._assert_str_attribute (attributes_dict , AWS_SPAN_KIND , span_kind )
546579
@@ -562,6 +595,7 @@ def _assert_semantic_conventions_span_attributes(
562595 kwargs .get ("remote_operation" ),
563596 status_code ,
564597 kwargs .get ("request_specific_attributes" , {}),
598+ kwargs .get ("response_specific_attributes" , {}),
565599 )
566600
567601 # pylint: disable=unidiomatic-typecheck
@@ -572,6 +606,7 @@ def _assert_semantic_conventions_attributes(
572606 operation : str ,
573607 status_code : int ,
574608 request_specific_attributes : dict ,
609+ response_specific_attributes : dict ,
575610 ) -> None :
576611 attributes_dict : Dict [str , AnyValue ] = self ._get_attributes_dict (attributes_list )
577612 self ._assert_str_attribute (attributes_dict , SpanAttributes .RPC_METHOD , operation )
@@ -587,6 +622,13 @@ def _assert_semantic_conventions_attributes(
587622 self ._assert_int_attribute (attributes_dict , key , value )
588623 else :
589624 self ._assert_array_value_ddb_table_name (attributes_dict , key , value )
625+ for key , value in response_specific_attributes .items ():
626+ if isinstance (value , str ):
627+ self ._assert_str_attribute (attributes_dict , key , value )
628+ elif isinstance (value , int ):
629+ self ._assert_int_attribute (attributes_dict , key , value )
630+ else :
631+ self ._assert_array_value_ddb_table_name (attributes_dict , key , value )
590632
591633 @override
592634 def _assert_metric_attributes (
0 commit comments