aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.6k stars 3.9k forks source link

aws_sns: Add support for $or operator in subscription filter policies #30524

Open ross-bragg-sonarsource opened 4 months ago

ross-bragg-sonarsource commented 4 months ago

Describe the feature

https://docs.aws.amazon.com/sns/latest/dg/and-or-logic.html#or-operator

I would like to be able to use the "$or" operator for SNS subscription filters to filter out messages based on multiple combinations of message attributes.

Use Case

I would like to create the following filter policy on an SNS subscription:

{
  "$or": [
    {
      "attribute_1": [
        "value_1",
        "value_2"
      ]
    },
    {
      "attribute_1": [
        "value_3"
      ],
      "attribute_2": [
        "value_4"
      ]
    }
  ]
}

However, the filter_policy on an SNS subscription is a Mapping[str, SubscriptionFilter], which doesn't account for the "$or" operator which effectively takes a list of filter policies. https://docs.aws.amazon.com/cdk/api/v2/python/aws_cdk.aws_sns/Subscription.html

I can get my desired result, but I have to set the filter policy directly:

subscription = self._topic.add_subscription(
    aws_sns_subscriptions.SqsSubscription(
        self._queue,
    )
)

subscription.node.default_child.filter_policy = filter_policy

where filter_policy is the dictionary at the beginning of the section.

I was unable to find if this was already supported in the documentation.

Proposed Solution

No response

Other Information

No response

Acknowledgements

CDK version used

2.141.0

Environment details (OS name and version, etc.)

MacOS, Python 3.9.6

nmussy commented 4 months ago

I don't think we can forego a typing change to fix this issue, whatever we do will still break the { [attribute: string]: SubscriptionFilter } typing.

The least invasive change would be to add a static SubscriptionFilter.or method to sugarcoat the operator, but it might be worth also adding a static SubscriptionFilter.and method for the sake of consistency? I'm not sure this is the way to go, it does add some unecessary synthax and will require to check that there are no duplicate keys in the and(...filters: { [attribute: string]: SubscriptionFilter }[]) parameters.

type SubscriptionFilterMap = { [attribute: string]: SubscriptionFilter | SubscriptionFilterMap[] };

export interface SubscriptionOptions {
  // ...
  readonly filterPolicy? : SubscriptionFilterMap;
}

export class SubscriptionFilter {
  public static and(...filters: { [attribute: string]: SubscriptionFilter }[]) {
    // TODO check conflicting filter keys
    return Object.assign({}, ...filters);
  }

  public static or(...filters: { [attribute: string]: SubscriptionFilter }[]) {
    return { $or: filters };
  }

  // ...
}

Using the .or operator:

new sns.Subscription(stack, 'Subscription', {
  endpoint: 'endpoint',
  filterPolicy: SubscriptionFilter.or(
    {
      color: sns.SubscriptionFilter.stringFilter({ allowlist: ['red', 'green'] }),
      price: sns.SubscriptionFilter.numericFilter({ allowlist: [100] }),
    },
    {
      color: sns.SubscriptionFilter.stringFilter({ allowlist: ['green'] }),
      price: sns.SubscriptionFilter.numericFilter({ allowlist: [500] }),
    },
    { color: sns.SubscriptionFilter.stringFilter({ allowlist: ['purple'] }) },
  ),
  protocol: sns.SubscriptionProtocol.LAMBDA,
  topic,
});

And also using the .and operator:

new sns.Subscription(stack, 'Subscription', {
  endpoint: 'endpoint',
  filterPolicy: SubscriptionFilter.or(
    SubscriptionFilter.and(
      { color: sns.SubscriptionFilter.stringFilter({ allowlist: ['red', 'green'] }) },
      { price: sns.SubscriptionFilter.numericFilter({ allowlist: [100] }) },
    ),
    SubscriptionFilter.and(
      { color: sns.SubscriptionFilter.stringFilter({ allowlist: ['green'] }) },
      { price: sns.SubscriptionFilter.numericFilter({ allowlist: [500] }) },
    ),
    { color: sns.SubscriptionFilter.stringFilter({ allowlist: ['purple'] }) },
  ),
  protocol: sns.SubscriptionProtocol.LAMBDA,
  topic,
});

Let me know if you can come up with a cleaner solution!

omrib15 commented 1 month ago

any update on this? I really need to be able to us $or in my filters