Skip to content

Conversation

@wedgybo
Copy link
Contributor

@wedgybo wedgybo commented Sep 4, 2016

What did you implement:

Added the ability to customise the return codes used by APIGateway so that different strategies can be used when deciding what should be returned. i.e. JSON objects

How did you implement it:

Adapted the default codes to be customisable. Also fixed a bug with the defaults were CORS was not applied to anything other than the default return code.

How can we verify it:

See additions to the README.md file for an example on how to test

Todos:

  • Write tests
  • Fix linting errors
  • Provide verification config/commands/resources
  • Switch codes to statusCodes
  • Add response headers
  • Switch to a single response template
  • Decide on behaviour of default codes

Referencing Issues

#2024 #2025 #2099

@wedgybo
Copy link
Contributor Author

wedgybo commented Sep 4, 2016

Rough draft of what we're trying to do. I think with the new template setup there's a nice way to replace the codes for each event without having to do anything crazy inside this feature.

Would be good to get some thoughts.

@JulienWeiler
Copy link

@wedgybo I did not test the code but this is exactly what I would expect.

@flomotlik
Copy link
Contributor

I like this. A few things that imho should be different though:

  1. codes -> statusCodes Imho that makes it a little bit more descriptive what it actually is.
  2. We should add headers here as well as you might want to add a different header to a different HTTP response
  3. We don't need templates as Lambda always returns with application/json. The templates would only be necessary when supporting http proxies with APIG and the request can return different content types than application/json.

We should create the default 200 code first, then merge the others on top so if you want you can even overwrite existing 200 easily.

  response:
        headers:
          Content-Type: "'text/html'"
        template: $input.path('$')
        statusCodes:
             201:
                pattern: '' # Default response method
             409:
                pattern: '.*"statusCode":409,.*' # JSON response
                template:
                headers:
                     Content-Type: "'text/html'"

Thanks for bringing this up and jumping on a PR here, would definitely be a valuable feature.

@wedgybo
Copy link
Contributor Author

wedgybo commented Sep 5, 2016

Reet. Will update the PR with those suggestions

@wedgybo
Copy link
Contributor Author

wedgybo commented Sep 6, 2016

About the template mappings. What about support for XML. We could keep these in and simplify the process of making the API return the correct response for XML content. https://forums.aws.amazon.com/thread.jspa?messageID=649836

@flomotlik
Copy link
Contributor

I don't really want to treat XML specifically different than other content that gets returned (e.g. HTML although obviously similar/the same as)

I'm not sure if XML is that large a use case that we should build extra code into the framework, when its already pretty easy to do.

@wedgybo
Copy link
Contributor Author

wedgybo commented Sep 6, 2016

Roger. Wouldn't be difficult to change if it's something that's required further down the line. Will change it to default to application/json for now

@flomotlik
Copy link
Contributor

@wedgybo Now I think I get what you mean. Its actually not possible :D.

Basically the APIG -> Lambda integration sends an HTTP Request from API Gateway to Lambda, lambda runs the function and then the HTTP Request gets a response from lambda with its content (which is always json). That response is then pushed through the response models and mapping.

This http response from Lambda into APIG ALWAYS returns with Content-Type set to application/json. You can test this by setting a header as in this example: https://github.com/serverless/serverless/blob/master/docs/02-providers/aws/events/01-apigateway.md#using-custom-response-headers

Content-Type: integration.response.header.Content-Type

This sets the Content-Type of the APIG response to the same content type as the request to Lambda (always application/json). So adding any content-type mappings beside application/json for APIG->Lambda will do nothing, as application/json is always chosen.

This functionality inside of APIG makes sense when you look at the proxy feature. When you use APIG as a proxy and it talks to another service that service can respond with another content-type so then more mappings make sense.

The whole system makes sense, but it took me forever to really fully understand it.

@wedgybo wedgybo force-pushed the custom-codes-support branch 4 times, most recently from 85f7148 to 18cb777 Compare September 7, 2016 11:44
@wedgybo
Copy link
Contributor Author

wedgybo commented Sep 7, 2016

@flomotlik I think this is ready to be looked at again. I've made the changed recommended.

If you can comment on the refactoring and let me know if it suits the serverless style or not. It should flow a lot better now and it's pretty easy to see where to add things now.

Also I've updated the behaviour of the default status code, so feedback on that would be helpful.

@wedgybo wedgybo changed the title Initial custom codes support Custom codes support Sep 7, 2016
@wedgybo wedgybo changed the title Custom codes support Custom status codes support Sep 7, 2016

#### Custom status codes

You can override the detauls status codes supplied by serverless if you need to change the default code, add/remove codes, or change the templates and selection process that dictates what code is returned.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"detauls" -> "default"
"serverless" -> "Serverless"

const normalizedFunctionName = functionName[0].toUpperCase()
+ functionName.substr(1);
// Merge in any custom response config
if (event.http.response && event.http.response.statusCodes) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this make it impossible to keep default status codes, but overwrite some of them?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed, maybe it's best to use some sort of a merge functionality rather than if/statements.

@jonsharratt
Copy link
Contributor

Wicked, was just looking at implementing this myself to see if this is possible as I have a requirement for returning a 201 from a PUT operation. Would be great to get this in 💃

@pgasiorowski
Copy link

👍 Similar requirements here. In additional to 201 and 204 I also heavily reply upon 422 returning json-api error structure. I'm going to give this PR a try too.

};

const hasDefaultStatusCode = (statusCodes) =>
Object.keys(statusCodes).some((statusCode) => (statusCode.pattern === ''));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should be statusCodes[statusCode].pattern === ''

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Good spot

@pgasiorowski
Copy link

@wedgybo I've tested this PR extensively and found two small issues:

  1. One is the incorrect check statusCode.pattern === which should be statusCodes[statusCode].pattern ===
  2. The other is replacing the default status codes as mentioned by @flomotlik - this isn't actually a bad thing for my use cases as I prefer to specify all codes on my own due to api spec requirements.

Other then that, my use case (below) works nicely with this PR! Serverless team please merge 👍

  statusCodes:
    201:
      pattern: '' # Default response method
      template: $input.json('$.body')
      headers:
        Content-Type: "'application/vnd.api+json'"
    400:
      pattern: 'Bad Request.*'
      template: $input.path('$.errorMessage')
      headers:
        Content-Type: "'text/plain'"
    422:
      pattern: '^\{"errors".*'
      template: $input.path('$.errorMessage')
      headers:
        Content-Type: "'application/vnd.api+json'"
    500:
      pattern: 'Internal Server Error.*'
      template: $input.path('$.errorMessage')
      headers:
        Content-Type: "'text/plain'"

@flomotlik
Copy link
Contributor

@WooDzu @wedgybo We're currently working on another implementation here that will make it way easier to set the response codes. I can't go into more detail yet, but hope to be able to share something over the next week. This will supersede this PR, but I'll leave it open for now so we can compare once this other feature is released.

@pgasiorowski
Copy link

I'm dying to know any details. If you could share any info of the YAML API I'd greatly appreciate and will look into aligning serverless-offline to work with it.

@pmuens
Copy link
Contributor

pmuens commented Sep 30, 2016

Yep. We're working on the PROXY implementation (as @WooDzu mentioned) so that you get great defaults from AWS out of the box.

In the future we want to support both. The API Gateway lambda proxy and the old one where you can specify your own request / response configuration. Thus would be a great addition to it!

@wedgybo
Copy link
Contributor Author

wedgybo commented Oct 5, 2016

@WooDzu @jokeyrhyme @b-b3rn4rd Does this extra commit achieve what you guys would be looking for?

@b-b3rn4rd
Copy link

@wedgybo Yep, it does exactly what I was after. Thanks !

@jokeyrhyme
Copy link

Now that the proxy work has been merged ( #2185 ), should the work in this PR be reconsidered?

For my own use cases, I think I'd prefer the dynamic approach that the proxy work affords, rather than a static definition in the YAML. Although I'm sure there are use cases where the static approach is more suitable.

@wedgybo
Copy link
Contributor Author

wedgybo commented Oct 6, 2016

Yea I think the majority of people will use the proxy method. Myself included. The decision was made to include both for those that wanted to state specifics of what they want. #2014 (comment)

@wedgybo
Copy link
Contributor Author

wedgybo commented Oct 11, 2016

Is there anything else needing done here? Just I notice a few duplicates are coming in around this now

@wedgybo wedgybo force-pushed the custom-codes-support branch from f0e0311 to 3f96c0d Compare October 11, 2016 12:48
Copy link
Contributor

@eahefnawy eahefnawy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wedgybo I've been testing this for a while now, and I couldn't break it. Great job 😊 ... and thanks a lot for the refactoring 💃

G2M

@jonsharratt
Copy link
Contributor

Exciting 💃 need this one to get my project back on to the main Serverless release 👍

@flomotlik flomotlik merged commit f517b38 into serverless:master Oct 14, 2016
@flomotlik
Copy link
Contributor

Thanks @wedgybo !!!!!!

@jonsharratt
Copy link
Contributor

Sweet, been waiting for this one.

Nailed it!

@corydorning
Copy link

this probably isn't the place for this, but i can't seem to figure out how to implment the custom status codes based on the docs. posted an SO question seeking help but after a google search i landed here. anyone have any examples trying to do something similar to what i'm doing here:
https://stackoverflow.com/questions/62536275/serverless-response-templates-w-lambda-integration

Feel free to delete this if needed.

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.

10 participants