Repository: serverless/serverless (JavaScript)
Date: 2017-05-22 16:38:53+00:00
Task type: Bug Fix

Issue:
New HTTP/HTTP_PROXY integrations should not set default mapping templates
# This is a Bug Report

## Description

### Overview

The newly introduced APIG integration types HTTP and HTTP_PROXY are used, to forward requests
to HTTP endpoints, with or **without** APIG mapping.
The latter (which is the most common use case for these integration types) is not possible
with the current implementation, because Serverless (master branch) unconditionally adds
its default mapping templates to the endpoint.

### Sample

Set up an HTTP integration type endpoint, with https://sample.url/myfwd as target.
We assume here, that the target URI only can cope with `x-www-form-urlencoded` data,
and our endpoint just should delegate to this URI.

If you now call our endpoint with `Content-Type: x-www-form-urlencoded` the target
endpoint will receive the data as JSON, because SLS added a mapping for this content type,
that converts it to JSON. As a consequence the call is rejected by the target endpoint.

The expectation is, that with a default deployment of HTTP and HTTP_PROXY endpoints
no mapping templates are set on the endpoint method. In this case mappings should
only be set in place, if the user explicitly adds some to the endpoint definition.

### Solution

Serverless should not add any default mapping templates in case one of the HTTP or 
HTTP_PROXY integrations are selected. This will send the incoming request unchanged 
to the target endpoint - as is expected.

The other integration types are ok with default templates as they guarantee that a lambda
endpoint will work, regardless what you throw at it.

## Additional Data

* Serverless Framework Version: **current master**



Issue discussion:
CC @eahefnawy 
Yes, that was an oversight in my previous PR which added `http` and `mock`. Once a mapping template is set (in this case the `x-form-urlencoded`) its transformation will be applied and cannot be disabled via Serverless. The only way seems to _not_ set any default mappings as @HyperBrain suggests.

Solution patch:
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 a9100bbdd5d..fa67bb8ba1a 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
@@ -83,7 +83,7 @@ module.exports = {
     if (type === 'AWS' || type === 'HTTP' || type === 'MOCK') {
       _.assign(integration, {
         PassthroughBehavior: http.request && http.request.passThrough,
-        RequestTemplates: this.getIntegrationRequestTemplates(http),
+        RequestTemplates: this.getIntegrationRequestTemplates(http, type === 'AWS'),
         IntegrationResponses: this.getIntegrationResponses(http),
       });
     }
@@ -148,19 +148,24 @@ module.exports = {
     return integrationResponses;
   },
 
-  getIntegrationRequestTemplates(http) {
+  getIntegrationRequestTemplates(http, useDefaults) {
     // default request templates
-    const integrationRequestTemplates = {
-      'application/json': this.DEFAULT_JSON_REQUEST_TEMPLATE,
-      'application/x-www-form-urlencoded': this.DEFAULT_FORM_URL_ENCODED_REQUEST_TEMPLATE,
-    };
+    const integrationRequestTemplates = {};
+
+    // Only set defaults for AWS (lambda) integration
+    if (useDefaults) {
+      _.assign(integrationRequestTemplates, {
+        'application/json': this.DEFAULT_JSON_REQUEST_TEMPLATE,
+        'application/x-www-form-urlencoded': this.DEFAULT_FORM_URL_ENCODED_REQUEST_TEMPLATE,
+      });
+    }
 
     // set custom request templates if provided
     if (http.request && typeof http.request.template === 'object') {
       _.assign(integrationRequestTemplates, http.request.template);
     }
 
-    return integrationRequestTemplates;
+    return !_.isEmpty(integrationRequestTemplates) ? integrationRequestTemplates : undefined;
   },
 
   DEFAULT_JSON_REQUEST_TEMPLATE: `
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js
index 365cafe8e6e..682502c1e57 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.js
@@ -389,7 +389,10 @@ module.exports = {
       return http.request.passThrough;
     }
 
-    return requestPassThroughBehaviors[0];
+    // Validate() sets the passThrough default to NEVER. This is inappropriate
+    // for HTTP and MOCK integrations, where there is no default request template defined.
+    const type = http.integration || 'AWS_PROXY';
+    return type === 'AWS' ? requestPassThroughBehaviors[0] : 'WHEN_NO_MATCH';
   },
 
   getResponse(http) {


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 8116320acad..da991486d0e 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
@@ -205,6 +205,10 @@ describe('#compileMethods()', () => {
         awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
           .Resources.ApiGatewayMethodUsersCreatePost.Properties.Integration.IntegrationHttpMethod
       ).to.equal('POST');
+      expect(
+        awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
+          .Resources.ApiGatewayMethodUsersCreatePost.Properties.Integration.RequestTemplates
+      ).to.equal(undefined);
     });
   });
 
diff --git a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.test.js b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.test.js
index 433126e1fa3..fcbbb1e4de8 100644
--- a/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.test.js
+++ b/lib/plugins/aws/package/compile/events/apiGateway/lib/validate.test.js
@@ -1297,7 +1297,7 @@ describe('#validate()', () => {
     expect(validated.events[0].http.request.passThrough).to.equal('WHEN_NO_MATCH');
   });
 
-  it('should default pass through to NEVER', () => {
+  it('should default pass through to NEVER for lambda', () => {
     awsCompileApigEvents.serverless.service.functions = {
       first: {
         events: [
@@ -1316,4 +1316,28 @@ describe('#validate()', () => {
     expect(validated.events).to.be.an('Array').with.length(1);
     expect(validated.events[0].http.request.passThrough).to.equal('NEVER');
   });
+
+  it('should not set default pass through http', () => {
+    awsCompileApigEvents.serverless.service.functions = {
+      first: {
+        events: [
+          {
+            http: {
+              method: 'GET',
+              path: 'users/list',
+              integration: 'http',
+              integrationMethod: 'GET',
+              request: {
+                uri: 'http://my.uri/me',
+              },
+            },
+          },
+        ],
+      },
+    };
+
+    const validated = awsCompileApigEvents.validate();
+    expect(validated.events).to.be.an('Array').with.length(1);
+    expect(validated.events[0].http.request.passThrough).to.equal(undefined);
+  });
 });