Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Feature: implement list rule names by target #12632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion localstack-core/localstack/services/events/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,29 @@ def list_rule_names_by_target(
limit: LimitMax100 = None,
**kwargs,
) -> ListRuleNamesByTargetResponse:
raise NotImplementedError
region = context.region
account_id = context.account_id
store = self.get_store(region, account_id)
event_bus_name = extract_event_bus_name(event_bus_name)
event_bus = self.get_event_bus(event_bus_name, store)

# Find all rules that have a target with the specified ARN
matching_rule_names = []
for rule_name, rule in event_bus.rules.items():
for target_id, target in rule.targets.items():
if target["Arn"] == target_arn:
matching_rule_names.append(rule_name)
break # Found a match in this rule, no need to check other targets

limited_rules, next_token = self._get_limited_list_and_next_token(
matching_rule_names, next_token, limit
)

response = ListRuleNamesByTargetResponse(RuleNames=limited_rules)
if next_token is not None:
response["NextToken"] = next_token

return response

@handler("PutRule")
def put_rule(
Expand Down Expand Up @@ -1514,6 +1536,24 @@ def _get_limited_dict_and_next_token(
)
return limited_dict, next_token

def _get_limited_list_and_next_token(
self, input_list: list, next_token: NextToken | None, limit: LimitMax100 | None
) -> tuple[list, NextToken]:
"""Return a slice of the given list starting from next_token with length of limit
and new last index encoded as a next_token for pagination."""
input_list_len = len(input_list)
start_index = decode_next_token(next_token) if next_token is not None else 0
end_index = start_index + limit if limit is not None else input_list_len
limited_list = input_list[start_index:end_index]

next_token = (
encode_next_token(end_index)
# return a next_token (encoded integer of next starting index) if not all items are returned
if end_index < input_list_len
else None
)
return limited_list, next_token

def _check_resource_exists(
self, resource_arn: Arn, resource_type: ResourceType, store: EventsStore
) -> None:
Expand Down
205 changes: 205 additions & 0 deletions tests/aws/services/events/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,211 @@ def test_process_pattern_to_single_matching_rules_single_target(
)
snapshot.match(f"events-{num_events}", events)

@markers.aws.validated
@pytest.mark.parametrize("bus_name", ["custom", "default"])
def test_list_rule_names_by_target(
self,
bus_name,
events_create_event_bus,
events_put_rule,
sqs_create_queue,
sqs_get_queue_arn,
aws_client,
snapshot,
clean_up,
):
"""Test the ListRuleNamesByTarget API to verify it correctly returns rules associated with a target."""
# Create an SQS queue to use as a target
queue_name = f"queue-{short_uid()}"
queue_url = sqs_create_queue(QueueName=queue_name)
queue_arn = sqs_get_queue_arn(queue_url)

# Create an event bus if using custom bus
if bus_name == "custom":
bus_name = f"bus-{short_uid()}"
events_create_event_bus(Name=bus_name)

# Create multiple rules targeting the same SQS queue
rule_prefix = f"rule-{short_uid()}-"
snapshot.add_transformer(snapshot.transform.regex(rule_prefix, "<rule-prefix>"))
rule_names = []

# Create 3 rules all targeting the same SQS queue
for i in range(3):
rule_name = f"{rule_prefix}{i}"
rule_names.append(rule_name)
events_put_rule(
Name=rule_name,
EventBusName=bus_name,
EventPattern=json.dumps({"source": [f"source-{i}"]}),
)

# Add the SQS queue as a target for this rule
target_id = f"target-{i}"
aws_client.events.put_targets(
Rule=rule_name,
EventBusName=bus_name,
Targets=[{"Id": target_id, "Arn": queue_arn}],
)

# Create a rule targeting a different resource (to verify filtering)
other_rule = f"{rule_prefix}other"
events_put_rule(
Name=other_rule,
EventBusName=bus_name,
EventPattern=json.dumps({"source": ["other-source"]}),
)

# Test the ListRuleNamesByTarget API
response = aws_client.events.list_rule_names_by_target(
TargetArn=queue_arn,
EventBusName=bus_name,
)

# The response should contain all rules that target our queue
snapshot.match("list_rule_names_by_target", response)

@markers.aws.validated
@pytest.mark.parametrize("bus_name", ["custom", "default"])
def test_list_rule_names_by_target_with_limit(
self,
bus_name,
events_create_event_bus,
events_put_rule,
sqs_create_queue,
sqs_get_queue_arn,
aws_client,
snapshot,
clean_up,
):
"""Test the ListRuleNamesByTarget API with pagination to verify it correctly handles limits and next tokens."""
# Create an SQS queue to use as a target
queue_name = f"queue-{short_uid()}"
queue_url = sqs_create_queue(QueueName=queue_name)
queue_arn = sqs_get_queue_arn(queue_url)

# Create an event bus if using custom bus
if bus_name == "custom":
bus_name = f"bus-{short_uid()}"
events_create_event_bus(Name=bus_name)

# Create multiple rules targeting the same SQS queue
rule_prefix = f"rule-{short_uid()}-"
snapshot.add_transformer(snapshot.transform.regex(rule_prefix, "<rule-prefix>"))
rule_names = []

# Create 5 rules all targeting the same SQS queue
for i in range(5):
rule_name = f"{rule_prefix}{i}"
rule_names.append(rule_name)
events_put_rule(
Name=rule_name,
EventBusName=bus_name,
EventPattern=json.dumps({"source": [f"source-{i}"]}),
)

# Add the SQS queue as a target for this rule
target_id = f"target-{i}"
aws_client.events.put_targets(
Rule=rule_name,
EventBusName=bus_name,
Targets=[{"Id": target_id, "Arn": queue_arn}],
)

# Test pagination with limit=2
all_rule_names = []
next_token = None

# First page
response = aws_client.events.list_rule_names_by_target(
TargetArn=queue_arn,
EventBusName=bus_name,
Limit=2,
)
# Store the original NextToken value before replacing it for snapshot comparison
next_token = response["NextToken"]
snapshot.add_transformer(
snapshot.transform.jsonpath(
jsonpath="$..NextToken",
value_replacement="<next-token>",
reference_replacement=True,
)
)

snapshot.match("first_page", response)
all_rule_names.extend(response["RuleNames"])

# Second page
response = aws_client.events.list_rule_names_by_target(
TargetArn=queue_arn,
EventBusName=bus_name,
Limit=2,
NextToken=next_token,
)
# Store the original NextToken value before replacing it for snapshot comparison
next_token = response["NextToken"]
snapshot.match("second_page", response)
all_rule_names.extend(response["RuleNames"])

# Third page (should have 1 remaining)
response = aws_client.events.list_rule_names_by_target(
TargetArn=queue_arn,
EventBusName=bus_name,
Limit=2,
NextToken=next_token,
)
snapshot.match("third_page", response)
all_rule_names.extend(response["RuleNames"])

@markers.aws.validated
@pytest.mark.parametrize("bus_name", ["custom", "default"])
def test_list_rule_names_by_target_no_matches(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Is there a particular reason this deviates from the cleanup structure in your other tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason...

To be completely honest, looks like I "forgot" to do it for this test.

In hindsight, after reading your other comment above about cleanups, I now realise that this test is actually the correct one in that as long as resources are created using the existing fixtures, those resources are already tracked and automatically cleaned up at the end of the test.

self,
bus_name,
events_create_event_bus,
events_put_rule,
sqs_create_queue,
sqs_get_queue_arn,
aws_client,
snapshot,
clean_up,
):
"""Test that ListRuleNamesByTarget returns empty result when no rules match the target."""
# Create two SQS queues
search_queue_url = sqs_create_queue()
search_queue_arn = sqs_get_queue_arn(search_queue_url)

target_queue_url = sqs_create_queue()
target_queue_arn = sqs_get_queue_arn(target_queue_url)

# Create event bus if needed
if bus_name == "custom":
bus_name = f"bus-{short_uid()}"
events_create_event_bus(Name=bus_name)

# Create rules targeting the target queue, but none targeting the search queue
rule_name = f"rule-{short_uid()}"
events_put_rule(
Name=rule_name,
EventBusName=bus_name,
EventPattern=json.dumps({"source": ["test-source"]}),
)

# Add the target
aws_client.events.put_targets(
Rule=rule_name,
EventBusName=bus_name,
Targets=[{"Id": "target-1", "Arn": target_queue_arn}],
)

# Test the ListRuleNamesByTarget API with the search queue ARN
response = aws_client.events.list_rule_names_by_target(
TargetArn=search_queue_arn,
EventBusName=bus_name,
)

snapshot.match("list_rule_names_by_target_no_matches", response)


class TestEventPattern:
@markers.aws.validated
Expand Down
Loading