Skip to content
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

Feature request: ability to specify JMESPath custom functions for Idempotency #2368

Open
1 of 2 tasks
dreamorosi opened this issue Apr 16, 2024 · 2 comments
Open
1 of 2 tasks
Labels
confirmed The scope is clear, ready for implementation feature-request This item refers to a feature request for an existing or new utility help-wanted We would really appreciate some support from community for this one idempotency This item relates to the Idempotency Utility

Comments

@dreamorosi
Copy link
Contributor

Use case

When working with the JMESPath expressions, the Idempotency utility uses some custom functions (i.e. powertools_json()) that extend the JMESPath built-in functions. This allows customers to work with some complex types that are common when working with AWS payloads.

The underlying JMESPath utility used by Idempotency however allows for further customization by allowing customers to set additional custom functions in addition or instead of the Powertools-provided ones.

It would be great if customers were able to pass their own subclass of Functions or PowertoolsFunctions when using the Idempotency utility.

Solution/User Experience

Currently the Idempotency utility creates its own instance of the PowertoolsFunctions class when instantiating the IdempotencyConfig class. This allows the various components of the utility to reuse it across the implementation.

The setting could be exposed to customers by adding a new option to the IdempotencyConfig class/object:

import { makeIdempotent, IdempotencyConfig } from '@aws-lambda-powertools/idempotency';
import { DynamoDBPersistenceLayer } from '@aws-lambda-powertools/idempotency/dynamodb';

const persistenceStore = new DynamoDBPersistenceLayer({
  tableName: 'idempotencyTableName',
});

class MyFancyFunctions extends PowertoolsFunctions {
  @Functions.signature({
    argumentsSpecs: [['string']],
  })
  public funcMyFancyFunction(value: string): JSONValue {
    return JSON.parse(value);
  }
}

export const handler = makeIdempotent(async () => true, {
  persistenceStore,
  config: new IdempotencyConfig({
    eventKeyJmespath: 'my_fancy_function(body).["user", "productId"]',
    jmesPathOptions: new MyFancyFunctions(), // passed as part of the idempotency configs
  }),
});

Alternative solutions

No response

Acknowledgment

Future readers

Please react with 👍 and your use case to help us understand customer demand.

@dreamorosi dreamorosi added idempotency This item relates to the Idempotency Utility feature-request This item refers to a feature request for an existing or new utility discussing The issue needs to be discussed, elaborated, or refined labels Apr 16, 2024
@leandrodamascena
Copy link
Contributor

Hello @dreamorosi! I think this might cover some user cases where the customer might need to decode a compressed string and use as the idempotent key, or want to add some information to the jmespath_key, we never know all the use cases. We support the customer in adding additional functions in Python.

from dataclasses import dataclass

from aws_lambda_powertools.utilities.idempotency import (
    DynamoDBPersistenceLayer,
    IdempotencyConfig,
    idempotent_function,
)
from aws_lambda_powertools.utilities.typing import LambdaContext

from jmespath.functions import signature

from aws_lambda_powertools.utilities.jmespath_utils import (
    PowertoolsFunctions
)

class CustomFunctions(PowertoolsFunctions):
    # only decode if value is a string
    # see supported data types: https://jmespath.org/specification.html#built-in-functions
    @signature({"types": ["string"]})
    def _func_custom_transform(self, payload: str):
        print("Custom function invoked")
        return "customfunction"


custom_jmespath_options = {"custom_functions": CustomFunctions()}

dynamodb = DynamoDBPersistenceLayer(table_name="ddbidempotency")
config = IdempotencyConfig(event_key_jmespath="custom_transform(order_id)", jmespath_options=custom_jmespath_options)  # see Choosing a payload subset section

@dataclass
class OrderItem:
    sku: str
    description: str

@dataclass
class Order:
    item: OrderItem
    order_id: str

@idempotent_function(data_keyword_argument="order", config=config, persistence_store=dynamodb)
def process_order(order: Order):
    return f"processed order {order.order_id}"

def lambda_handler(event: dict, context: LambdaContext):
    config.register_lambda_context(context)  # see Lambda timeouts section
    order_item = OrderItem(sku="fake", description="sample")
    order = Order(item=order_item, order_id="testid")

    # `order` parameter must be called as a keyword argument to work
    process_order(order=order)

Thanks

@dreamorosi
Copy link
Contributor Author

Thank you Leo, we'll add this to the backlog.

I'm also adding the help-wanted label to signal that this issue is open for contribution. If anyone wants to pick this up please leave a comment and feel free to ask any question.

@dreamorosi dreamorosi added help-wanted We would really appreciate some support from community for this one confirmed The scope is clear, ready for implementation and removed discussing The issue needs to be discussed, elaborated, or refined labels Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed The scope is clear, ready for implementation feature-request This item refers to a feature request for an existing or new utility help-wanted We would really appreciate some support from community for this one idempotency This item relates to the Idempotency Utility
Projects
Development

No branches or pull requests

2 participants