Aws-sam-cli: Inline swagger with x-amazon-apigateway-any-method doesn't work with "ANY" method API event

Created on 2 Feb 2018  路  7Comments  路  Source: aws/aws-sam-cli

I'm encountering an issue on SAM Local v0.2.6 that doesn't occur when deploying the SAM template to AWS.

Given the following template.yml:

---
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Parameters:
Resources:
  Function:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: function.handler
      Runtime: python3.6
      CodeUri: './src'
      Events:
        RootRequest:
          Type: Api
          Properties:
            Path: /
            Method: ANY
            RestApiId: !Ref FunctionApi
  FunctionApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      DefinitionBody:
        swagger: "2.0"
        info:
          title: MyFunctionApi
        paths:
          /:
            x-amazon-apigateway-any-method:
              responses: {}
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub:
                    - arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/${FunctionArn}/invocations
                    - { FunctionArn: !GetAtt Function.Arn }

The output from sam local start-api includes the following:

WARNING: Could not find function for [OPTIONS] to / resource
WARNING: Could not find function for [GET] to / resource
WARNING: Could not find function for [HEAD] to / resource
WARNING: Could not find function for [POST] to / resource
WARNING: Could not find function for [PUT] to / resource
WARNING: Could not find function for [DELETE] to / resource
WARNING: Could not find function for [PATCH] to / resource
Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [OPTIONS GET HEAD POST PUT DELETE PATCH]

The response code from curl http://localhost:3000 is 502 Bad Gateway and the body is:

{ "message": "No function defined for resource method" }

I should add that the above works fine when deployed to a real Cloud Formation stack. It just behaves differently in SAM Local.

My current workaround to this issue is to add an API event to the function for each HTTP method, i.e.

      Events:
        RootRequestPost:
          Type: Api
          Properties:
            Path: /
            Method: POST
            RestApiId: !Ref FunctionApi
        RootRequestGet:
          Type: Api
          Properties:
            Path: /
            Method: GET
            RestApiId: !Ref FunctionApi
        RootRequestOptions:
          Type: Api
          Properties:
            Path: /
            Method: OPTIONS
            RestApiId: !Ref FunctionApi
        RootRequestHead:
          Type: Api
          Properties:
            Path: /
            Method: HEAD
            RestApiId: !Ref FunctionApi
        RootRequestDelete:
          Type: Api
          Properties:
            Path: /
            Method: DELETE
            RestApiId: !Ref FunctionApi
        RootRequestPatch:
          Type: Api
          Properties:
            Path: /
            Method: PATCH
            RestApiId: !Ref FunctionApi
        RootRequestPut:
          Type: Api
          Properties:
            Path: /
            Method: PUT
            RestApiId: !Ref FunctionApi

Then the output of sam local start-api includes:

Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [OPTIONS]
Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [GET]
Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [HEAD]
Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [POST]
Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [PUT]
Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [DELETE]
Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [PATCH]

I can then curl the API with no problems.

Having looked at the SAM Local code, I can see that when a Swagger definition is parsed, the x-amazon-apigateway-any-method operation results in separate HTTP methods (GET, POST, etc) being added to the NewServerlessRouter. Conversely, when an API function event with an ANY method is parsed, only a single ANY method is added to NewServerlessRouter. The subsequent merge operation to consolidate mounts and handlers therefore fails to map the multiple Swagger methods to the single function method handler.

Could this be fixed by changing the merge logic in NewServerlessRouter to be more intelligent about which mounts should be merged when an 'ANY' mount is encountered?

Most helpful comment

Hi,
Any updates on this?
Thanks!

All 7 comments

An additional complication - the workaround mentioned above only works locally. Deploying it, Cloud Formation creates permissions in the Lambda function policy which are specific to each HTTP verb, e.g.:

{
  "Effect": "Allow",
  "Principal": {
    "Service": "apigateway.amazonaws.com"
  },
  "Action": "lambda:invokeFunction",
  "Resource": "arn:aws:lambda:us-east-1:**********:function:MyFunction
  "Condition": {
    "ArnLike": {
      "AWS:SourceArn": "arn:aws:execute-api:us-east-1:**********:**********/MyStage/GET/"
    }
  }
}

Since an ANY method is still created in API Gateway and that method handles all requests, API requests fail due to API Gateway not having the right permissions to invoke the Lambda function. An additional permission is therefore required on the function policy, e.g.:

{
  "Effect": "Allow",
  "Principal": {
    "Service": "apigateway.amazonaws.com"
  },
  "Action": "lambda:invokeFunction",
  "Resource": "arn:aws:lambda:us-east-1:**********:function:MyFunction
  "Condition": {
    "ArnLike": {
      "AWS:SourceArn": "arn:aws:execute-api:us-east-1:**********:**********/MyStage/ANY/"
    }
  }
}

Currently, to ensure local and cloud can both work from a single template, I need to define a function API event per HTTP method and one for the ANY method, in other words:

  Events:
    RootRequestPost:
      Type: Api
      Properties:
        Path: /
        Method: POST
        RestApiId: !Ref FunctionApi
    RootRequestGet:
      Type: Api
      Properties:
        Path: /
        Method: GET
        RestApiId: !Ref FunctionApi
    RootRequestOptions:
      Type: Api
      Properties:
        Path: /
        Method: OPTIONS
        RestApiId: !Ref FunctionApi
    RootRequestHead:
      Type: Api
      Properties:
        Path: /
        Method: HEAD
        RestApiId: !Ref FunctionApi
    RootRequestDelete:
      Type: Api
      Properties:
        Path: /
        Method: DELETE
        RestApiId: !Ref FunctionApi
    RootRequestPatch:
      Type: Api
      Properties:
        Path: /
        Method: PATCH
        RestApiId: !Ref FunctionApi
    RootRequestPut:
      Type: Api
      Properties:
        Path: /
        Method: PUT
        RestApiId: !Ref FunctionApi
    RootRequestAny:
      Type: Api
      Properties:
        Path: /
        Method: ANY
        RestApiId: !Ref FunctionApi

This is pretty ugly.

Hi,
Any updates on this?
Thanks!

+1 ... also having this exact same issue on 0.2.11.

I also got exactly the same problem.

@StefanSmith This was corrected in the 0.3.0 release. Closing since this has been addressed.

We even have integ tests for it: https://github.com/awslabs/aws-sam-cli/blob/develop/tests/integration/local/start_api/test_start_api.py#L180 :)

This is now working for me as expected @jfuss.

Thanks!

thanks @StefanSmith for documenting this issue. Searched high and low for how to set up the ANYmethod assigned to the /{proxy+} path pattern and your documentation of x-amazon-apigateway-any-method helped me.

I added the following to my SAM API resource

/{proxy+}:
 x-amazon-apigateway-any-method:
  x-amazon-apigateway-integration:
   httpMethod: POST
   type: aws_proxy
   uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambdaResource.Arn}/invocations
                passthroughBehavior: "when_no_match"
  responses: {}

and added the following to my SAM Lambda Resource

AnyMethodForProxy:
 Type: Api
 Properties:
  Path: /{proxy+}
  Method: any
  RestApiId: !Ref MyApiResource
Was this page helpful?
0 / 5 - 0 ratings

Related issues

terlar picture terlar  路  3Comments

asyba picture asyba  路  3Comments

debuggins picture debuggins  路  4Comments

zhangzhx picture zhangzhx  路  3Comments

rhlsthrm picture rhlsthrm  路  4Comments