SNS:subscribe() now has increased support for the FilterPolicy-argument (#5436)
This commit is contained in:
parent
89008c4230
commit
d145471b3f
@ -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(
|
||||||
|
@ -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"})
|
||||||
|
Loading…
Reference in New Issue
Block a user