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

Inline swagger with x-amazon-apigateway-any-method doesn't work with "ANY" method API event #289

Closed
StefanSmith opened this issue Feb 1, 2018 · 7 comments

Comments

@StefanSmith
Copy link
Contributor

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?

@StefanSmith
Copy link
Contributor Author

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.

@StefanSmith
Copy link
Contributor Author

Hi,
Any updates on this?
Thanks!

@domZippilli
Copy link

domZippilli commented Apr 12, 2018

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

@bigunyak
Copy link

bigunyak commented May 7, 2018

I also got exactly the same problem.

@jfuss
Copy link
Contributor

jfuss commented May 10, 2018

@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 :)

@jfuss jfuss closed this as completed May 10, 2018
@araleius
Copy link

araleius commented May 22, 2018

This is now working for me as expected @jfuss.

Thanks!

@averysmithproductions
Copy link

averysmithproductions commented Apr 6, 2020

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

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

No branches or pull requests

6 participants