{"action":"created","issue":{"url":"https://api.github.com/repos/awslabs/aws-sam-local/issues/289","repository_url":"https://api.github.com/repos/awslabs/aws-sam-local","labels_url":"https://api.github.com/repos/awslabs/aws-sam-local/issues/289/labels{/name}","comments_url":"https://api.github.com/repos/awslabs/aws-sam-local/issues/289/comments","events_url":"https://api.github.com/repos/awslabs/aws-sam-local/issues/289/events","html_url":"https://github.com/awslabs/aws-sam-local/issues/289","id":293721072,"number":289,"title":"Inline swagger with x-amazon-apigateway-any-method doesn't work with \"ANY\" method API event","user":{"login":"StefanSmith","id":227291,"avatar_url":"https://avatars0.githubusercontent.com/u/227291?v=4","gravatar_id":"","url":"https://api.github.com/users/StefanSmith","html_url":"https://github.com/StefanSmith","followers_url":"https://api.github.com/users/StefanSmith/followers","following_url":"https://api.github.com/users/StefanSmith/following{/other_user}","gists_url":"https://api.github.com/users/StefanSmith/gists{/gist_id}","starred_url":"https://api.github.com/users/StefanSmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/StefanSmith/subscriptions","organizations_url":"https://api.github.com/users/StefanSmith/orgs","repos_url":"https://api.github.com/users/StefanSmith/repos","events_url":"https://api.github.com/users/StefanSmith/events{/privacy}","received_events_url":"https://api.github.com/users/StefanSmith/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-02-01T23:28:04Z","updated_at":"2018-02-02T22:33:54Z","closed_at":null,"author_association":"NONE","body":"I'm encountering an issue on SAM Local v0.2.6 that doesn't occur when deploying the SAM template to AWS.\r\n\r\nGiven the following template.yml:\r\n\r\n    ---\r\n    AWSTemplateFormatVersion: '2010-09-09'\r\n    Transform: 'AWS::Serverless-2016-10-31'\r\n    Parameters:\r\n    Resources:\r\n      Function:\r\n        Type: 'AWS::Serverless::Function'\r\n        Properties:\r\n          Handler: function.handler\r\n          Runtime: python3.6\r\n          CodeUri: './src'\r\n          Events:\r\n            RootRequest:\r\n              Type: Api\r\n              Properties:\r\n                Path: /\r\n                Method: ANY\r\n                RestApiId: !Ref FunctionApi\r\n      FunctionApi:\r\n        Type: AWS::Serverless::Api\r\n        Properties:\r\n          StageName: Prod\r\n          DefinitionBody:\r\n            swagger: \"2.0\"\r\n            info:\r\n              title: MyFunctionApi\r\n            paths:\r\n              /:\r\n                x-amazon-apigateway-any-method:\r\n                  responses: {}\r\n                  x-amazon-apigateway-integration:\r\n                    httpMethod: POST\r\n                    type: aws_proxy\r\n                    uri:\r\n                      Fn::Sub:\r\n                        - arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/${FunctionArn}/invocations\r\n                        - { FunctionArn: !GetAtt Function.Arn }\r\n\r\n\r\nThe output from `sam local start-api` includes the following:\r\n\r\n    WARNING: Could not find function for [OPTIONS] to / resource\r\n    WARNING: Could not find function for [GET] to / resource\r\n    WARNING: Could not find function for [HEAD] to / resource\r\n    WARNING: Could not find function for [POST] to / resource\r\n    WARNING: Could not find function for [PUT] to / resource\r\n    WARNING: Could not find function for [DELETE] to / resource\r\n    WARNING: Could not find function for [PATCH] to / resource\r\n    Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [OPTIONS GET HEAD POST PUT DELETE PATCH]\r\n\r\nThe response code from `curl http://localhost:3000` is `502 Bad Gateway` and the body is:\r\n\r\n    { \"message\": \"No function defined for resource method\" }\r\n\r\nI should add that the above works fine when deployed to a real Cloud Formation stack. It just behaves differently in SAM Local.\r\n\r\nMy current workaround to this issue is to add an API event to the function for each HTTP method, i.e.\r\n\r\n          Events:\r\n            RootRequestPost:\r\n              Type: Api\r\n              Properties:\r\n                Path: /\r\n                Method: POST\r\n                RestApiId: !Ref FunctionApi\r\n            RootRequestGet:\r\n              Type: Api\r\n              Properties:\r\n                Path: /\r\n                Method: GET\r\n                RestApiId: !Ref FunctionApi\r\n            RootRequestOptions:\r\n              Type: Api\r\n              Properties:\r\n                Path: /\r\n                Method: OPTIONS\r\n                RestApiId: !Ref FunctionApi\r\n            RootRequestHead:\r\n              Type: Api\r\n              Properties:\r\n                Path: /\r\n                Method: HEAD\r\n                RestApiId: !Ref FunctionApi\r\n            RootRequestDelete:\r\n              Type: Api\r\n              Properties:\r\n                Path: /\r\n                Method: DELETE\r\n                RestApiId: !Ref FunctionApi\r\n            RootRequestPatch:\r\n              Type: Api\r\n              Properties:\r\n                Path: /\r\n                Method: PATCH\r\n                RestApiId: !Ref FunctionApi\r\n            RootRequestPut:\r\n              Type: Api\r\n              Properties:\r\n                Path: /\r\n                Method: PUT\r\n                RestApiId: !Ref FunctionApi\r\n\r\nThen the output of `sam local start-api` includes:\r\n\r\n    Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [OPTIONS]\r\n    Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [GET]\r\n    Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [HEAD]\r\n    Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [POST]\r\n    Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [PUT]\r\n    Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [DELETE]\r\n    Mounting function.handler (python3.6) at http://127.0.0.1:3000/ [PATCH]\r\n\r\nI can then curl the API with no problems.\r\n\r\nHaving 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.\r\n\r\nCould 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?"},"comment":{"url":"https://api.github.com/repos/awslabs/aws-sam-local/issues/comments/362727831","html_url":"https://github.com/awslabs/aws-sam-local/issues/289#issuecomment-362727831","issue_url":"https://api.github.com/repos/awslabs/aws-sam-local/issues/289","id":362727831,"user":{"login":"StefanSmith","id":227291,"avatar_url":"https://avatars0.githubusercontent.com/u/227291?v=4","gravatar_id":"","url":"https://api.github.com/users/StefanSmith","html_url":"https://github.com/StefanSmith","followers_url":"https://api.github.com/users/StefanSmith/followers","following_url":"https://api.github.com/users/StefanSmith/following{/other_user}","gists_url":"https://api.github.com/users/StefanSmith/gists{/gist_id}","starred_url":"https://api.github.com/users/StefanSmith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/StefanSmith/subscriptions","organizations_url":"https://api.github.com/users/StefanSmith/orgs","repos_url":"https://api.github.com/users/StefanSmith/repos","events_url":"https://api.github.com/users/StefanSmith/events{/privacy}","received_events_url":"https://api.github.com/users/StefanSmith/received_events","type":"User","site_admin":false},"created_at":"2018-02-02T22:33:54Z","updated_at":"2018-02-02T22:33:54Z","author_association":"NONE","body":"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.:\r\n\r\n    {\r\n      \"Effect\": \"Allow\",\r\n      \"Principal\": {\r\n        \"Service\": \"apigateway.amazonaws.com\"\r\n      },\r\n      \"Action\": \"lambda:invokeFunction\",\r\n      \"Resource\": \"arn:aws:lambda:us-east-1:**********:function:MyFunction\r\n      \"Condition\": {\r\n        \"ArnLike\": {\r\n          \"AWS:SourceArn\": \"arn:aws:execute-api:us-east-1:**********:**********/MyStage/GET/\"\r\n        }\r\n      }\r\n    }\r\n\r\nSince 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.:\r\n\r\n    {\r\n      \"Effect\": \"Allow\",\r\n      \"Principal\": {\r\n        \"Service\": \"apigateway.amazonaws.com\"\r\n      },\r\n      \"Action\": \"lambda:invokeFunction\",\r\n      \"Resource\": \"arn:aws:lambda:us-east-1:**********:function:MyFunction\r\n      \"Condition\": {\r\n        \"ArnLike\": {\r\n          \"AWS:SourceArn\": \"arn:aws:execute-api:us-east-1:**********:**********/MyStage/ANY/\"\r\n        }\r\n      }\r\n    }\r\n\r\nCurrently, 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:\r\n\r\n      Events:\r\n        RootRequestPost:\r\n          Type: Api\r\n          Properties:\r\n            Path: /\r\n            Method: POST\r\n            RestApiId: !Ref FunctionApi\r\n        RootRequestGet:\r\n          Type: Api\r\n          Properties:\r\n            Path: /\r\n            Method: GET\r\n            RestApiId: !Ref FunctionApi\r\n        RootRequestOptions:\r\n          Type: Api\r\n          Properties:\r\n            Path: /\r\n            Method: OPTIONS\r\n            RestApiId: !Ref FunctionApi\r\n        RootRequestHead:\r\n          Type: Api\r\n          Properties:\r\n            Path: /\r\n            Method: HEAD\r\n            RestApiId: !Ref FunctionApi\r\n        RootRequestDelete:\r\n          Type: Api\r\n          Properties:\r\n            Path: /\r\n            Method: DELETE\r\n            RestApiId: !Ref FunctionApi\r\n        RootRequestPatch:\r\n          Type: Api\r\n          Properties:\r\n            Path: /\r\n            Method: PATCH\r\n            RestApiId: !Ref FunctionApi\r\n        RootRequestPut:\r\n          Type: Api\r\n          Properties:\r\n            Path: /\r\n            Method: PUT\r\n            RestApiId: !Ref FunctionApi\r\n        RootRequestAny:\r\n          Type: Api\r\n          Properties:\r\n            Path: /\r\n            Method: ANY\r\n            RestApiId: !Ref FunctionApi\r\n\r\nThis is pretty ugly."}}
{"actor":{"display_login":"StefanSmith"}}