SNS:subscribe() now has increased support for the FilterPolicy-argument (#5436)

This commit is contained in:
Bert Blommers 2022-08-31 11:12:14 +00:00 committed by GitHub
parent 89008c4230
commit d145471b3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 228 additions and 5 deletions

View File

@ -265,8 +265,6 @@ class Subscription(BaseModel):
) )
def _matches_filter_policy(self, message_attributes): def _matches_filter_policy(self, message_attributes):
# TODO: support Anything-but matching, prefix matching and
# numeric value matching.
if not self._filter_policy: if not self._filter_policy:
return True return True
@ -311,12 +309,75 @@ class Subscription(BaseModel):
return True return True
if isinstance(rule, dict): if isinstance(rule, dict):
keyword = list(rule.keys())[0] keyword = list(rule.keys())[0]
attributes = list(rule.values())[0] value = list(rule.values())[0]
if keyword == "exists": if keyword == "exists":
if attributes and field in message_attributes: if value and field in message_attributes:
return True return True
elif not attributes and field not in message_attributes: elif not value and field not in message_attributes:
return True return True
elif keyword == "prefix" and isinstance(value, str):
if field in message_attributes:
attr = message_attributes[field]
if attr["Type"] == "String" and attr["Value"].startswith(
value
):
return True
elif keyword == "anything-but":
if field not in message_attributes:
continue
attr = message_attributes[field]
if isinstance(value, dict):
# We can combine anything-but with the prefix-filter
anything_but_key = list(value.keys())[0]
anything_but_val = list(value.values())[0]
if anything_but_key != "prefix":
return False
if attr["Type"] == "String":
actual_values = [attr["Value"]]
else:
actual_values = [v for v in attr["Value"]]
if all(
[
not v.startswith(anything_but_val)
for v in actual_values
]
):
return True
else:
undesired_values = (
[value] if isinstance(value, str) else value
)
if attr["Type"] == "Number":
actual_values = [str(attr["Value"])]
elif attr["Type"] == "String":
actual_values = [attr["Value"]]
else:
actual_values = [v for v in attr["Value"]]
if all([v not in undesired_values for v in actual_values]):
return True
elif keyword == "numeric" and isinstance(value, list):
# [(< x), (=, y), (>=, z)]
numeric_ranges = zip(value[0::2], value[1::2])
if (
message_attributes.get(field, {}).get("Type", "")
== "Number"
):
msg_value = message_attributes[field]["Value"]
matches = []
for operator, test_value in numeric_ranges:
test_value = int(test_value)
if operator == ">":
matches.append((msg_value > test_value))
if operator == ">=":
matches.append((msg_value >= test_value))
if operator == "=":
matches.append((msg_value == test_value))
if operator == "<":
matches.append((msg_value < test_value))
if operator == "<=":
matches.append((msg_value <= test_value))
return all(matches)
attr = message_attributes[field]
return False return False
return all( return all(

View File

@ -1120,3 +1120,165 @@ def test_filtering_all_AND_matching_no_match():
message_bodies.should.equal([]) message_bodies.should.equal([])
message_attributes = [json.loads(m.body)["MessageAttributes"] for m in messages] message_attributes = [json.loads(m.body)["MessageAttributes"] for m in messages]
message_attributes.should.equal([]) message_attributes.should.equal([])
@mock_sqs
@mock_sns
def test_filtering_prefix():
topic, queue = _setup_filter_policy_test(
{"customer_interests": [{"prefix": "bas"}]}
)
for interest, idx in [("basketball", "1"), ("rugby", "2"), ("baseball", "3")]:
topic.publish(
Message=f"match{idx}",
MessageAttributes={
"customer_interests": {"DataType": "String", "StringValue": interest},
},
)
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)["Message"] for m in messages]
set(message_bodies).should.equal({"match1", "match3"})
@mock_sqs
@mock_sns
def test_filtering_anything_but():
topic, queue = _setup_filter_policy_test(
{"customer_interests": [{"anything-but": "basketball"}]}
)
for interest, idx in [("basketball", "1"), ("rugby", "2"), ("baseball", "3")]:
topic.publish(
Message=f"match{idx}",
MessageAttributes={
"customer_interests": {"DataType": "String", "StringValue": interest},
},
)
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)["Message"] for m in messages]
set(message_bodies).should.equal({"match2", "match3"})
@mock_sqs
@mock_sns
def test_filtering_anything_but_multiple_values():
topic, queue = _setup_filter_policy_test(
{"customer_interests": [{"anything-but": ["basketball", "rugby"]}]}
)
for interest, idx in [("basketball", "1"), ("rugby", "2"), ("baseball", "3")]:
topic.publish(
Message=f"match{idx}",
MessageAttributes={
"customer_interests": {"DataType": "String", "StringValue": interest},
},
)
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)["Message"] for m in messages]
set(message_bodies).should.equal({"match3"})
@mock_sqs
@mock_sns
def test_filtering_anything_but_prefix():
topic, queue = _setup_filter_policy_test(
{"customer_interests": [{"anything-but": {"prefix": "bas"}}]}
)
for interest, idx in [("basketball", "1"), ("rugby", "2"), ("baseball", "3")]:
topic.publish(
Message=f"match{idx}",
MessageAttributes={
"customer_interests": {"DataType": "String", "StringValue": interest},
},
)
# This should match rugby only
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)["Message"] for m in messages]
set(message_bodies).should.equal({"match2"})
@mock_sqs
@mock_sns
def test_filtering_anything_but_unknown():
topic, queue = _setup_filter_policy_test(
{"customer_interests": [{"anything-but": {"unknown": "bas"}}]}
)
for interest, idx in [("basketball", "1"), ("rugby", "2"), ("baseball", "3")]:
topic.publish(
Message=f"match{idx}",
MessageAttributes={
"customer_interests": {"DataType": "String", "StringValue": interest},
},
)
# This should match rugby only
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)["Message"] for m in messages]
message_bodies.should.equal([])
@mock_sqs
@mock_sns
def test_filtering_anything_but_numeric():
topic, queue = _setup_filter_policy_test(
{"customer_interests": [{"anything-but": ["100"]}]}
)
for nr, idx in [("50", "1"), ("100", "2"), ("150", "3")]:
topic.publish(
Message=f"match{idx}",
MessageAttributes={
"customer_interests": {"DataType": "Number", "StringValue": nr},
},
)
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)["Message"] for m in messages]
set(message_bodies).should.equal({"match1", "match3"})
@mock_sqs
@mock_sns
def test_filtering_numeric_match():
topic, queue = _setup_filter_policy_test(
{"customer_interests": [{"numeric": ["=", "100"]}]}
)
for nr, idx in [("50", "1"), ("100", "2"), ("150", "3")]:
topic.publish(
Message=f"match{idx}",
MessageAttributes={
"customer_interests": {"DataType": "Number", "StringValue": nr},
},
)
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)["Message"] for m in messages]
set(message_bodies).should.equal({"match2"})
@mock_sqs
@mock_sns
def test_filtering_numeric_range():
topic, queue = _setup_filter_policy_test(
{"customer_interests": [{"numeric": [">", "49", "<=", "100"]}]}
)
for nr, idx in [("50", "1"), ("100", "2"), ("150", "3")]:
topic.publish(
Message=f"match{idx}",
MessageAttributes={
"customer_interests": {"DataType": "Number", "StringValue": nr},
},
)
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)["Message"] for m in messages]
set(message_bodies).should.equal({"match1", "match2"})