Add exact Number, exact String.Array and attribute key matching to SNS subscription filter policy and validate filter policy

This commit is contained in:
gruebel 2019-08-25 16:48:14 +02:00
commit d8a922811c
5 changed files with 564 additions and 8 deletions

View file

@ -109,6 +109,17 @@ def test_publish_to_sqs_bad():
}})
except ClientError as err:
err.response['Error']['Code'].should.equal('InvalidParameterValue')
try:
# Test Number DataType, with a non numeric value
conn.publish(
TopicArn=topic_arn, Message=message,
MessageAttributes={'price': {
'DataType': 'Number',
'StringValue': 'error'
}})
except ClientError as err:
err.response['Error']['Code'].should.equal('InvalidParameterValue')
err.response['Error']['Message'].should.equal("An error occurred (ParameterValueInvalid) when calling the Publish operation: Could not cast message attribute 'price' value to number.")
@mock_sqs
@ -487,3 +498,380 @@ def test_filtering_exact_string_no_attributes_no_match():
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([])
@mock_sqs
@mock_sns
def test_filtering_exact_number_int():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [100]})
topic.publish(
Message='match',
MessageAttributes={'price': {'DataType': 'Number',
'StringValue': '100'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal(
[{'price': {'Type': 'Number', 'Value': 100}}])
@mock_sqs
@mock_sns
def test_filtering_exact_number_float():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [100.1]})
topic.publish(
Message='match',
MessageAttributes={'price': {'DataType': 'Number',
'StringValue': '100.1'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal(
[{'price': {'Type': 'Number', 'Value': 100.1}}])
@mock_sqs
@mock_sns
def test_filtering_exact_number_float_accuracy():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [100.123456789]})
topic.publish(
Message='match',
MessageAttributes={'price': {'DataType': 'Number',
'StringValue': '100.1234561'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal(
[{'price': {'Type': 'Number', 'Value': 100.1234561}}])
@mock_sqs
@mock_sns
def test_filtering_exact_number_no_match():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [100]})
topic.publish(
Message='no match',
MessageAttributes={'price': {'DataType': 'Number',
'StringValue': '101'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal([])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([])
@mock_sqs
@mock_sns
def test_filtering_exact_number_with_string_no_match():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [100]})
topic.publish(
Message='no match',
MessageAttributes={'price': {'DataType': 'String',
'StringValue': '100'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal([])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([])
@mock_sqs
@mock_sns
def test_filtering_string_array_match():
topic, subscription, queue = _setup_filter_policy_test(
{'customer_interests': ['basketball', 'baseball']})
topic.publish(
Message='match',
MessageAttributes={'customer_interests': {'DataType': 'String.Array',
'StringValue': json.dumps(['basketball', 'rugby'])}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal(
[{'customer_interests': {'Type': 'String.Array', 'Value': json.dumps(['basketball', 'rugby'])}}])
@mock_sqs
@mock_sns
def test_filtering_string_array_no_match():
topic, subscription, queue = _setup_filter_policy_test(
{'customer_interests': ['baseball']})
topic.publish(
Message='no_match',
MessageAttributes={'customer_interests': {'DataType': 'String.Array',
'StringValue': json.dumps(['basketball', 'rugby'])}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal([])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([])
@mock_sqs
@mock_sns
def test_filtering_string_array_with_number_match():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [100, 500]})
topic.publish(
Message='match',
MessageAttributes={'price': {'DataType': 'String.Array',
'StringValue': json.dumps([100, 50])}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal(
[{'price': {'Type': 'String.Array', 'Value': json.dumps([100, 50])}}])
@mock_sqs
@mock_sns
def test_filtering_string_array_with_number_float_accuracy_match():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [100.123456789, 500]})
topic.publish(
Message='match',
MessageAttributes={'price': {'DataType': 'String.Array',
'StringValue': json.dumps([100.1234561, 50])}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal(
[{'price': {'Type': 'String.Array', 'Value': json.dumps([100.1234561, 50])}}])
@mock_sqs
@mock_sns
# this is the correct behavior from SNS
def test_filtering_string_array_with_number_no_array_match():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [100, 500]})
topic.publish(
Message='match',
MessageAttributes={'price': {'DataType': 'String.Array',
'StringValue': '100'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal(
[{'price': {'Type': 'String.Array', 'Value': '100'}}])
@mock_sqs
@mock_sns
def test_filtering_string_array_with_number_no_match():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [500]})
topic.publish(
Message='no_match',
MessageAttributes={'price': {'DataType': 'String.Array',
'StringValue': json.dumps([100, 50])}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal([])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([])
@mock_sqs
@mock_sns
# this is the correct behavior from SNS
def test_filtering_string_array_with_string_no_array_no_match():
topic, subscription, queue = _setup_filter_policy_test(
{'price': [100]})
topic.publish(
Message='no_match',
MessageAttributes={'price': {'DataType': 'String.Array',
'StringValue': 'one hundread'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal([])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([])
@mock_sqs
@mock_sns
def test_filtering_attribute_key_exists_match():
topic, subscription, queue = _setup_filter_policy_test(
{'store': [{'exists': True}]})
topic.publish(
Message='match',
MessageAttributes={'store': {'DataType': 'String',
'StringValue': 'example_corp'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal(
[{'store': {'Type': 'String', 'Value': 'example_corp'}}])
@mock_sqs
@mock_sns
def test_filtering_attribute_key_exists_no_match():
topic, subscription, queue = _setup_filter_policy_test(
{'store': [{'exists': True}]})
topic.publish(
Message='no match',
MessageAttributes={'event': {'DataType': 'String',
'StringValue': 'order_cancelled'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal([])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([])
@mock_sqs
@mock_sns
def test_filtering_attribute_key_not_exists_match():
topic, subscription, queue = _setup_filter_policy_test(
{'store': [{'exists': False}]})
topic.publish(
Message='match',
MessageAttributes={'event': {'DataType': 'String',
'StringValue': 'order_cancelled'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal(
[{'event': {'Type': 'String', 'Value': 'order_cancelled'}}])
@mock_sqs
@mock_sns
def test_filtering_attribute_key_not_exists_no_match():
topic, subscription, queue = _setup_filter_policy_test(
{'store': [{'exists': False}]})
topic.publish(
Message='no match',
MessageAttributes={'store': {'DataType': 'String',
'StringValue': 'example_corp'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal([])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([])
@mock_sqs
@mock_sns
def test_filtering_all_AND_matching_match():
topic, subscription, queue = _setup_filter_policy_test(
{'store': [{'exists': True}],
'event': ['order_cancelled'],
'customer_interests': ['basketball', 'baseball'],
'price': [100]})
topic.publish(
Message='match',
MessageAttributes={'store': {'DataType': 'String',
'StringValue': 'example_corp'},
'event': {'DataType': 'String',
'StringValue': 'order_cancelled'},
'customer_interests': {'DataType': 'String.Array',
'StringValue': json.dumps(['basketball', 'rugby'])},
'price': {'DataType': 'Number',
'StringValue': '100'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal(
['match'])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([{
'store': {'Type': 'String', 'Value': 'example_corp'},
'event': {'Type': 'String', 'Value': 'order_cancelled'},
'customer_interests': {'Type': 'String.Array', 'Value': json.dumps(['basketball', 'rugby'])},
'price': {'Type': 'Number', 'Value': 100}}])
@mock_sqs
@mock_sns
def test_filtering_all_AND_matching_no_match():
topic, subscription, queue = _setup_filter_policy_test(
{'store': [{'exists': True}],
'event': ['order_cancelled'],
'customer_interests': ['basketball', 'baseball'],
'price': [100],
"encrypted": [False]})
topic.publish(
Message='no match',
MessageAttributes={'store': {'DataType': 'String',
'StringValue': 'example_corp'},
'event': {'DataType': 'String',
'StringValue': 'order_cancelled'},
'customer_interests': {'DataType': 'String.Array',
'StringValue': json.dumps(['basketball', 'rugby'])},
'price': {'DataType': 'Number',
'StringValue': '100'}})
messages = queue.receive_messages(MaxNumberOfMessages=5)
message_bodies = [json.loads(m.body)['Message'] for m in messages]
message_bodies.should.equal([])
message_attributes = [
json.loads(m.body)['MessageAttributes'] for m in messages]
message_attributes.should.equal([])

View file

@ -201,7 +201,9 @@ def test_creating_subscription_with_attributes():
"store": ["example_corp"],
"event": ["order_cancelled"],
"encrypted": [False],
"customer_interests": ["basketball", "baseball"]
"customer_interests": ["basketball", "baseball"],
"price": [100, 100.12],
"error": [None]
})
conn.subscribe(TopicArn=topic_arn,
@ -294,7 +296,9 @@ def test_set_subscription_attributes():
"store": ["example_corp"],
"event": ["order_cancelled"],
"encrypted": [False],
"customer_interests": ["basketball", "baseball"]
"customer_interests": ["basketball", "baseball"],
"price": [100, 100.12],
"error": [None]
})
conn.set_subscription_attributes(
SubscriptionArn=subscription_arn,
@ -332,6 +336,77 @@ def test_set_subscription_attributes():
)
@mock_sns
def test_subscribe_invalid_filter_policy():
conn = boto3.client('sns', region_name = 'us-east-1')
conn.create_topic(Name = 'some-topic')
response = conn.list_topics()
topic_arn = response['Topics'][0]['TopicArn']
try:
conn.subscribe(TopicArn = topic_arn,
Protocol = 'http',
Endpoint = 'http://example.com/',
Attributes = {
'FilterPolicy': json.dumps({
'store': [str(i) for i in range(151)]
})
})
except ClientError as err:
err.response['Error']['Code'].should.equal('InvalidParameter')
err.response['Error']['Message'].should.equal('Invalid parameter: FilterPolicy: Filter policy is too complex')
try:
conn.subscribe(TopicArn = topic_arn,
Protocol = 'http',
Endpoint = 'http://example.com/',
Attributes = {
'FilterPolicy': json.dumps({
'store': [['example_corp']]
})
})
except ClientError as err:
err.response['Error']['Code'].should.equal('InvalidParameter')
err.response['Error']['Message'].should.equal('Invalid parameter: FilterPolicy: Match value must be String, number, true, false, or null')
try:
conn.subscribe(TopicArn = topic_arn,
Protocol = 'http',
Endpoint = 'http://example.com/',
Attributes = {
'FilterPolicy': json.dumps({
'store': [{'exists': None}]
})
})
except ClientError as err:
err.response['Error']['Code'].should.equal('InvalidParameter')
err.response['Error']['Message'].should.equal('Invalid parameter: FilterPolicy: exists match pattern must be either true or false.')
try:
conn.subscribe(TopicArn = topic_arn,
Protocol = 'http',
Endpoint = 'http://example.com/',
Attributes = {
'FilterPolicy': json.dumps({
'store': [{'error': True}]
})
})
except ClientError as err:
err.response['Error']['Code'].should.equal('InvalidParameter')
err.response['Error']['Message'].should.equal('Invalid parameter: FilterPolicy: Unrecognized match type error')
try:
conn.subscribe(TopicArn = topic_arn,
Protocol = 'http',
Endpoint = 'http://example.com/',
Attributes = {
'FilterPolicy': json.dumps({
'store': [1000000001]
})
})
except ClientError as err:
err.response['Error']['Code'].should.equal('InternalFailure')
@mock_sns
def test_check_not_opted_out():
conn = boto3.client('sns', region_name='us-east-1')