Serverless-application-model: Inject StageVariable into API resource definition that uses a Swagger file

Created on 21 Nov 2016  路  14Comments  路  Source: aws/serverless-application-model

When using a Swagger file to define an API, users have to explicitly specify Stage Variables to hold the Lambda function ARN and use the stage variable in their Swagger template to specify the integration. This is not intuitive.

We could simplify this process by automatically injecting the Stage Variable when users specify an API Event source to their Lambda Function

When customers specify a template like this:

LambdaFunction:
  Type: AWS::Serverless::Function
  Properties:
      Handler: index.handler
      Runtime: nodejs4.3
      Events:
         GetApi:
             Type: API
              Properties:
                  Path: /
                  Method: GET
                  RestApiId: !Ref MyApiResource

MyApiResource:
   Type: AWS::Serverless::Api
   Properties:
        DefinitionUri: s3://bucket/myswagger.yaml

we could inject a StageVariable to the API resource where variable name is equal to the function resource's logical name and value equal to the function's ARN

MyApiResource:
   Type: AWS::Serverless::Api
   Properties:
        DefinitionUri: s3://bucket/myswagger.yaml
        Variables:
             LambdaFunction: !Ref LambdaFunction
areserverless-api areswagger typfeature wontfix

Most helpful comment

Finally got it working! In my SAM template:

Api:
  Type: AWS::Serverless::Api
  Properties:
    StageName: Latest
    DefinitionBody:
      Fn::Transform:
        Name: AWS::Include
        Parameters:
          Location: !Sub s3://${ArtifactsBucket}/swagger.yaml

where ArtifactsBucket refers to the bucket where I upload my Swagger spec prior to 'packaging' the SAM template. Then, in the Swagger template itself I can use the intrinsics, e.g.

x-amazon-apigateway-integration:
  type: aws_proxy
  httpMethod: POST
  uri:
    Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetProjects.Arn}/invocations

I'm using the long Fn::Sub notation here instead of just !Sub, because Swagger doesn't natively support the latter, and also because the docs on AWS::Include Transform say that shorthand forms are not supported yet.

On a side note, just wasted ~4 hours troubleshooting the Invalid ARN specified in the request error, thinking that Fn::Sub for function ARNs doesn't work. Turns out, I only forgot to prepend an ARN reference for authorizerCredentials role in the spec (I'm using a custom authorizer). I really wish API Gateway import errors were more informative! Like specifying the concrete line that fails.. In any case, just make sure to prepend Fn::Sub/Fn::Ref/Fn::GetAtt to ALL of your CloudFormation references in the spec.

P.S. Not sure about local references, that would be awesome though!

All 14 comments

_examples/2016-10-31/api_swagger_cors_ seems to inject the function logical name. Could we somehow get the full ARN such that we don't have to hard-code region and accountId into Swagger (or defining them as stage variables) as well? Thanks

Yes, getting the full ARN would be useful. This will solve my problem with using a stage variable for the account ID - #87

Our current cheapo workaround (since we cannot use the inlined body directly in other tools like Swagger Editor, or for input validation) is to maintain a separate swagger.yaml, but format everything as if it was inside the SAM template (i.e. we can reference parameters through ${} syntax).

We then terminate the actual SAM template with the Api resource, but instead of specifying a value of the DefinitionBody as the last parameter (after an obvious !Sub |-), we concatenate the Swagger file before packaging the application (YAML lets you do that if you also properly indent all lines of the Swagger file, either explicitly in the file or using a regular expression in gulp-replace or something similar).

Nice, it seems like now we will be able to use http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/create-reusable-transform-function-snippets-and-add-to-your-template-with-aws-include-transform.html to directly insert the Swagger YAML stored on S3 (but before any substitutions are made). Will report back once I test it.

Eager to see what possible with include transform, wonder if they will allow you to point to a local template like aws package does.

Finally got it working! In my SAM template:

Api:
  Type: AWS::Serverless::Api
  Properties:
    StageName: Latest
    DefinitionBody:
      Fn::Transform:
        Name: AWS::Include
        Parameters:
          Location: !Sub s3://${ArtifactsBucket}/swagger.yaml

where ArtifactsBucket refers to the bucket where I upload my Swagger spec prior to 'packaging' the SAM template. Then, in the Swagger template itself I can use the intrinsics, e.g.

x-amazon-apigateway-integration:
  type: aws_proxy
  httpMethod: POST
  uri:
    Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetProjects.Arn}/invocations

I'm using the long Fn::Sub notation here instead of just !Sub, because Swagger doesn't natively support the latter, and also because the docs on AWS::Include Transform say that shorthand forms are not supported yet.

On a side note, just wasted ~4 hours troubleshooting the Invalid ARN specified in the request error, thinking that Fn::Sub for function ARNs doesn't work. Turns out, I only forgot to prepend an ARN reference for authorizerCredentials role in the spec (I'm using a custom authorizer). I really wish API Gateway import errors were more informative! Like specifying the concrete line that fails.. In any case, just make sure to prepend Fn::Sub/Fn::Ref/Fn::GetAtt to ALL of your CloudFormation references in the spec.

P.S. Not sure about local references, that would be awesome though!

Any update on using local reference with AWS::Include?

Also, I tried passing in the Lambda ARN and the region as stage variables as shown above by @sanathkr, but this did not work. Still got an "Invalid ARN specified in the request" error. Is this pattern no longer supported?

I tried the stage variables approach too and it did not work for me either. Will try the approach @dinvlad suggested.

@dinvlad your approach looks great, but is there a way to avoid the manual swagger file upload? I would rather reference a local file (as I'm currently doing with DefinitionUri), and I'd expect cloudformation package to take care of the packaging for me.

Does it make sense? Am I missing something obvious?

@alexcasalboni agreed, that would be awesome. I don't think CF currently supports that though. package might though, but that is part of CLI so perhaps we should ask there.

Also, you can paste your entire swagger inside CF template, and reference variables the same way. But that blows up the size of the SAM template, which is currently limited to 51.2 KB (last time I checked).

@dinvlad Yes, that's what I'm doing right now. Inlining a small API sounded reasonable.
And thanks for the template size limit, that's good to know!

Note that, at least for me, the method @dinvlad recommends doesn't work if the Include'd Swagger file is > whatever the size limit is. Gets the following error in CF console:

Errors found during import: Unable to put integration on 'GET' for resource at path '/account': The item is too large. Please reduce the item size.

Template size limit is 460KB if you upload it to S3 and pass the S3 URL to CloudFormation.

I don't see us doing this at the moment for external swagger files. Best is to either inline the Swagger file or use AWS::Include to let CloudFormation inline the file on runtime.

Was this page helpful?
0 / 5 - 0 ratings