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

feat(function-transformer): add support for event invocation types (async lambda) #2864

Merged
merged 5 commits into from
Sep 12, 2024

Conversation

atierian
Copy link
Member

Changes already reviewed in


Description of changes

Adds support for AppSync asynchronous Lambda function invocations via the @function directive.

API Changes

directive @function(
  name: String!,
  region: String,
  accountId: String,
+  invocationType: InvocationType = RequestResponse
)

+ enum InvocationType { 
+   RequestResponse
+   Event
+ }

Callouts

appendError vs error

The response mapping template for Event invocation types uses util.appendError rather than util.error used by RequestResponse (default) invocation types.

util.error has always been the behavior of the @function directive. I was unable to find any justification for the decision in the original PR:

util.error immediately exits preventing any further execution in the response (also request but n/a here) mapping template and subsequent resolvers in the same pipeline. This is reasonable in the context of single function resolvers, but is unnecessarily constraining in a multi-function pipeline -- using util.appendError could¹ allow the subsequent Lambda function to make a handling decision for the previous error based on business logic.

Changing this behavior for existing RequestResponse (default) @function directives would be a breaking change. However, we have an opportunity to move in a more flexible direction for the newly introduced Event based @function directives. We're using util.appendError in the response mapping template of Event @function directives for two reasons:

  1. It allows us to return the EventInvocationResponse regardless of whether the Lambda function execution failed or not.
{ 
  "data": { 
    "myMutation": { 
      "success": true
    }
  }
}
{ 
  "data": { 
    "myMutation": { 
      "success": false
    }
  },
  "errors": [
    {  }
  ]
}

For comparison, if Event based @function directives used util.error, this would be the failure response:

{ 
  "data": { 
    "myMutation": null
  },
  "errors": [
    {  }
  ]
}
  1. It allows execution of subsequent resolvers in the pipeline.

¹ This would require passing ctx.error in the Lambda invocation payload; this is not done today and is not included in this PR.

While we're on the topic, I'm going to document the behavior of util.error and util.appendError in pipeline resolvers for future reference.

  • util.error immediately exits, returning the error and only the error to the caller. Neither subsequent lines in the request/response mapping template nor subsequent resolvers in a pipeline are executed after util.error
  • util.appendError continues execution and returns the appended error in the errors block of the request.
  • Appending an error using util.appendError, then calling util.error results in the appended error being excluded in the errors block -- only the error passed to util.error is returned in the errors block.

Response Types

Using @function(name: 'name', invocationType: Event) requires that the response type of that field is EventInvocationResponse, which must be explicitly defined in the schema as:

type EventInvocationResponse {
  success: Boolean!
}

This applies when the Event invocation type @function directive is the final @function directive defined on that field. For example:

type Mutation {
  asyncStuff(msg: String): EventInvocationResponse @function(name: "stuff", invocationType: Event)
}
type Mutation {
  asyncStuff(msg: String): EventInvocationResponse  
  @function(name: "stuff")
  @function(name: "other-stuff", invocationType: Event)
}

But not when a 'RequestResponse' invocation is the final @function directive. This is fine:

type Mutation {
  asyncStuff(msg: String): String
  @function(name: "stuff", invocationType: Event)
  @function(name: "other-stuff")
}

Constraints

Event invocation type @function directives can only be used on Mutation or Query fields. This is is more constraining than RequestResponse (default) invocation types.

CDK / CloudFormation Parameters Changed

Issue #, if available

Description of how you validated changes

  • Snapshot testing.
  • Manually deployed and testing GraphQL API.

Note: This PR is into the feature/async-lambda branch where a tagged release will be cut. A full E2E test run will happen prior to merging this to main.

Checklist

  • PR description included
  • yarn test passes
  • Tests are changed or added
  • Relevant documentation is changed or added (and PR referenced)
  • New AWS SDK calls or CloudFormation actions have been added to relevant test and service IAM policies
  • Any CDK or CloudFormation parameter changes are called out explicitly

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@atierian atierian requested review from a team as code owners September 11, 2024 17:58
@atierian atierian changed the title Feature/async lambda feat(function-transformer): add support for event invocation types (async lambda) Sep 11, 2024
chore: merge main into feature/async-lambda
@atierian atierian merged commit 6a54bad into main Sep 12, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants