Repository: serverless/serverless (JavaScript)
Date: 2020-08-31 20:08:28+00:00
Task type: Feature

Issue:
Allow for the removal of default request mapping templates
Currently [default request mapping templates are created ](https://github.com/serverless/serverless/blob/v1.58.0/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js#L189) both for application/json and application/x-www-form-urlencoded. There does not appear to be a simple method for overriding this default. In our application we only support the application/json content type however application/x-www-form-urlencoded is being automatically created even though it is not supported by our application. Even worse, when generating swagger documentation via the AWS API Gateway this forces us advertise that we consume both:

"consumes": [
          "application/json",
          "application/x-www-form-urlencoded"
        ],

IMO, there really should be a way of overriding defaults in any application. If you can suggest any workaround to this it would be great



Issue discussion:
Indeed there seem no way to override that.

Some kind of fix would be to allow `null`  for configuration properties placed in `resources.Resources` and translate them to deletions.

We welcome PR on that
My personal opinion is that application/x-www-form-urlencoded is not a particularly useful default anyway. It seems a pity to have to null it everywhere. Would it not be nicer to simply use what IS defined in the .yml? i.e default unless something is specified. 
If there's a general agreement we could remove that default. But I think it'll mean a breaking change, and in that case cannot be shipped until we're ready to publish v2 (of which delivery date is uncertain)
Fair point. Maybe a more global setting to turn defaults off. This would reduce the need to keep nulling on every usage whilst maintaining backward compatibility (if defaulted to keep them on)
> Maybe a more global setting to turn defaults off

Yes, we can address that specifically with some configuration option.

Still when proposing special `null` handling, I was thinking about more generic way in which we can tweak generated configuration (it could serve as meantime workaround also for other similar problems, and not just that specific one).
I just noticed this too and want to wake this back up.

How about just disabling the default if a custom one is defined? This is still breaking... but I don't know a situation where you would rely on it.

I see 2 use cases:
1. You want a generic request with parameters in which case you actually do not care if the request body was json or url-encoded as long as the lambda gets the correct body.
2. You want a to actually map the request to the event object. In which case: any mapping you didn't define would be wrong and at best would result in some undefined error and at worst would create undefined behavior.

I currently experiment with use case 2 and also with validation on the apiGateway side. And there I have an additional problem, validation is also bound to the Content-Type so I now have a potential way of going around the validation wich isn't good either.

I look into it and see how easy it is to get a feature flag [there](https://github.com/serverless/serverless/blob/master/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js#L204).
> How about just disabling the default if a custom one is defined?

I've just checked and I believe you can override the default as following (what you can't do, is to remove the template completely):

```yaml
functions:
  someFunction:
    events:
      - http:
          request:
            template:
              application/json: 'custom template'
```
Yes, you can override them but the `application/x-www-form-urlencoded` is still present.
and @sihutch want(ed) to disable them completely for the passthough behavior.
I want to limit them to only `application/json` so I don't have potential security holes in my application though undefined behaviour.

So there needs to be some way to set [useDefaults](https://github.com/serverless/serverless/blob/master/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js#L203) to false.
The simplest way I suggested is to simply disable it when `typeof http.request.template === 'object'` (which is already present a few lines below that) but that would potentially be breaking if someone relied on adding an additional template to the defaults.

I'm fairly new to api gateway and therefore probably don't know all use cases but I know of at least the 2 where the current behaviour is either dangerous or unusable.

Edit: sorry, I seemed to have skipped reading your "what you can't do, is to remove the template completely"
@Nemo64 Assuming that overriding default with some value, is not good enough, I think non breaking way, is to support notation as:

```yaml
functions:
  someFunction:
    events:
      - http:
          request:
            template:
              application/x-www-form-urlencoded:  null
```

Through which we may indicate that we do not wish the default to be applied for given request type.

I think it's an acceptable solution, taking into account how invasive defaults are applied.

For v2.0 we may consider what you suggest, so to not apply defaults at all when `http.request` is provided, but I'd like to have more info on whether that's indeed desirable for all cases

Well one case I could think of where removing the defaults is not desirable is if you actually use the defaults and want to add for example `text/csv` and then parse that down to the same format as the other defaults would provide.

That would be a lot of template though as the default template emulates the lambda proxy behaviour and building one for another format that is compatible with the defaults... I don't know, I'd probably build a custom one for each format with just what I need as it would be a lot more obvious what I'm doing.
This is definitely an issue I'm having. It would be nice to not have default templates added if I defined the templates I want
Are there any updates on this one?
@Catastropha PR's welcome!
@medikoo I'll take this one! :raised_back_of_hand: 

Which approach do you guys think is best?

1. Setting each `[...].request.template['content-type']` to `null` or `false`
2. Setting `[...].request.ignoreDefaults` to `true`
3. Any third non-breaking idea?


Solution patch:
diff --git a/docs/providers/aws/events/apigateway.md b/docs/providers/aws/events/apigateway.md
index 77c99fa11b9..6680fc31cca 100644
--- a/docs/providers/aws/events/apigateway.md
+++ b/docs/providers/aws/events/apigateway.md
@@ -994,6 +994,22 @@ functions:
 You can then access the query string `https://example.com/dev/whatever?bar=123` by `event.foo` in the lambda function.
 If you want to spread a string into multiple lines, you can use the `>` or `|` syntax, but the following strings have to be all indented with the same amount, [read more about `>` syntax](http://stackoverflow.com/questions/3790454/in-yaml-how-do-i-break-a-string-over-multiple-lines).
 
+In order to remove one of the default request templates you just need to pass it as null, as follows:
+
+```yml
+functions:
+  create:
+    handler: posts.create
+    events:
+      - http:
+          method: get
+          path: whatever
+          integration: lambda
+          request:
+            template:
+              application/x-www-form-urlencoded: null
+```
+
 #### Pass Through Behavior
 
 [API Gateway](https://serverless.com/amazon-api-gateway/) provides multiple ways to handle requests where the Content-Type header does not match any of the specified mapping templates. When this happens, the request payload will either be passed through the integration request _without transformation_ or rejected with a `415 - Unsupported Media Type`, depending on the configuration.
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js
index 3be22495910..140b18edd6b 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/integration.js
@@ -209,7 +209,13 @@ module.exports = {
 
     // set custom request templates if provided
     if (http.request && typeof http.request.template === 'object') {
-      Object.assign(integrationRequestTemplates, http.request.template);
+      _.entries(http.request.template).forEach(([contentType, template]) => {
+        if (template === null) {
+          delete integrationRequestTemplates[contentType];
+        } else {
+          integrationRequestTemplates[contentType] = template;
+        }
+      });
     }
 
     return Object.keys(integrationRequestTemplates).length


Test patch:
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
index acfde4b271a..c1ab20a1c2e 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/method/index.test.js
@@ -1304,6 +1304,37 @@ describe('#compileMethods()', () => {
       });
     });
 
+    it('should delete the default "application/x-www-form-urlencoded" template if it\'s overriden with null', () => {
+      awsCompileApigEvents.validated.events = [
+        {
+          functionName: 'Second',
+          http: {
+            method: 'get',
+            path: 'users/list',
+            integration: 'AWS',
+            request: {
+              template: {
+                'application/x-www-form-urlencoded': null,
+              },
+            },
+            response: {
+              statusCodes: {
+                200: {
+                  pattern: '',
+                },
+              },
+            },
+          },
+        },
+      ];
+      return awsCompileApigEvents.compileMethods().then(() => {
+        expect(
+          awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate.Resources
+            .ApiGatewayMethodUsersListGet.Properties.Integration.RequestTemplates
+        ).to.not.have.key('application/x-www-form-urlencoded');
+      });
+    });
+
     it('should use defined pass-through behavior', () => {
       awsCompileApigEvents.validated.events = [
         {