Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions samcli/commands/validate/lib/sam_template_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
"""
import logging
import functools
from pathlib import Path

from samtranslator.public.exceptions import InvalidDocumentException
from samtranslator.parser import parser
from samtranslator.translator.translator import Translator
from boto3.session import Session

from samcli.lib.utils.packagetype import ZIP, IMAGE
from samcli.lib.utils.resources import AWS_SERVERLESS_FUNCTION
from samcli.yamlhelper import yaml_dump
from samcli.lib.utils.resources import AWS_SERVERLESS_FUNCTION, AWS_SERVERLESS_API, AWS_SERVERLESS_HTTPAPI
from samcli.yamlhelper import yaml_dump, parse_yaml_file
from .exceptions import InvalidSamDocumentException

LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -65,6 +66,7 @@ def is_valid(self):

self._replace_local_codeuri()
self._replace_local_image()
self._replace_local_openapi()

try:
template = sam_translator.translate(sam_template=self.sam_template, parameter_values={})
Expand Down Expand Up @@ -139,6 +141,33 @@ def _replace_local_image(self):
if "ImageUri" not in properties:
properties["ImageUri"] = "111111111111.dkr.ecr.region.amazonaws.com/repository"

def _replace_local_openapi(self):
"""
Applies Transform Include of DefinitionBody.
Without this validation fails.
"""
# look for transform that includes a yaml definition like this:
# DefinitionBody:
# 'Fn::Transform':
# Name: AWS::Include
# Parameters:
# Location: openapi.yaml
resources = self.sam_template.get("Resources", {})
for _, resource in resources.items():
if not resource.get("Type", "") in [AWS_SERVERLESS_API, AWS_SERVERLESS_HTTPAPI]:
continue

transform_dict = resource.get("Properties", {}).get("DefinitionBody", {}).get("Fn::Transform", {})
if transform_dict.get("Name", "") != "AWS::Include":
continue

location_prop = transform_dict.get("Parameters", {}).get("Location", "")
if not Path(location_prop).is_file():
LOG.debug("Couldn't find file %s to import definition body", location_prop)
continue

SamTemplateValidator._replace_with_file_contents(resource.get("Properties", {}), location_prop)

@staticmethod
def is_s3_uri(uri):
"""
Expand Down Expand Up @@ -180,3 +209,11 @@ def _update_to_s3_uri(property_key, resource_property_dict, s3_uri_value="s3://b
return

resource_property_dict[property_key] = s3_uri_value

@staticmethod
def _replace_with_file_contents(resource_property_dict, location_prop):
definition_body = parse_yaml_file(location_prop)
LOG.debug("Imported definition body from %s", location_prop)

# replace the definition body from the file
resource_property_dict["DefinitionBody"] = definition_body
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Resources:
Api:
Type: AWS::Serverless::HttpApi
Properties:
CorsConfiguration: True
DefinitionBody:
'Fn::Transform':
Name: AWS::Include
Parameters:
Location: tests/functional/commands/validate/lib/openapi/openapi.yaml
Function:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: python3.7
Events:
Api:
Type: HttpApi
6 changes: 6 additions & 0 deletions tests/functional/commands/validate/lib/openapi/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
openapi: 3.0.0
paths:
/test:
get:
summary: Get test
18 changes: 18 additions & 0 deletions tests/integration/testdata/validate/openapi_json/openapi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.0.0",
"title": "title"
},
"paths": {
"/test": {
"get": {
"responses": {
"200": {
"description": "description"
}
}
}
}
}
}
53 changes: 53 additions & 0 deletions tests/integration/testdata/validate/openapi_json/template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Resources": {
"ServerlessApi": {
"Type": "AWS::Serverless::Api",
"Properties": {
"StageName": "Prod",
"Auth": {
"DefaultAuthorizer": "Authorizer",
"Authorizers": {
"Authorizer": {
"AuthorizerPayloadFormatVersion": "2.0",
"FunctionArn": {
"Fn::ImportValue": "AuthorizerFunction"
}
}
}
},
"DefinitionBody": {
"Fn::Transform": {
"Name": "AWS::Include",
"Parameters": {"Location": "./openapi.json"}
}
}
}
},
"ServerlessHttpApi": {
"Type": "AWS::Serverless::HttpApi",
"Properties": {
"StageName": "Prod",
"Auth": {
"DefaultAuthorizer": "Authorizer",
"Authorizers": {
"Authorizer": {
"AuthorizerPayloadFormatVersion": "2.0",
"FunctionArn": {
"Fn::ImportValue": "AuthorizerFunction"
}
}
}
},
"CorsConfiguration": true,
"DefinitionBody": {
"Fn::Transform": {
"Name": "AWS::Include",
"Parameters": {"Location": "./openapi.json"}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
openapi: 3.0.0
paths:
/test:
get:
summary: Get test
36 changes: 36 additions & 0 deletions tests/integration/testdata/validate/openapi_yaml/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: [AWS::Serverless-2016-10-31]
Resources:
ServerlessApi:
Type: AWS::Serverless::Api
Properties:
Auth:
DefaultAuthorizer: Authorizer
Authorizers:
Authorizer:
AuthorizerPayloadFormatVersion: '2.0'
FunctionArn:
Fn::ImportValue: AuthorizerFunction
DefinitionBody:
'Fn::Transform':
Name: AWS::Include
Parameters:
Location: ./openapi.yaml
StageName: Prod
ServerlessHttpApi:
Type: AWS::Serverless::HttpApi
Properties:
Auth:
DefaultAuthorizer: Authorizer
Authorizers:
Authorizer:
AuthorizerPayloadFormatVersion: '2.0'
FunctionArn:
Fn::ImportValue: AuthorizerFunction
CorsConfiguration: True
DefinitionBody:
'Fn::Transform':
Name: AWS::Include
Parameters:
Location: ./openapi.yaml
StageName: Prod
2 changes: 2 additions & 0 deletions tests/integration/validate/test_validate_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ def command_list(
[
("default_yaml", TemplateFileTypes.YAML), # project with template.yaml
("default_json", TemplateFileTypes.JSON), # project with template.json
("openapi_yaml", TemplateFileTypes.YAML), # project with template.yaml
("openapi_json", TemplateFileTypes.JSON), # project with template.json
("multiple_files", TemplateFileTypes.YAML), # project with both template.yaml and template.json
(
"with_build",
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/commands/local/lib/swagger/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,39 @@ def test_payload_format_version(self):

self.assertEqual(expected, result)

def test_payload_format_version_for_none(self):
function_name = "myfunction"
swagger = {
"paths": {
"/path1": {"get": {}},
"/path2": {"get": {}},
}
}

parser = SwaggerParser(self.stack_path, swagger)
parser._get_integration_function_name = Mock()
parser._get_integration_function_name.return_value = function_name

expected = [
Route(
path="/path1",
methods=["get"],
function_name=function_name,
payload_format_version="None",
stack_path=self.stack_path,
),
Route(
path="/path2",
methods=["get"],
function_name=function_name,
payload_format_version="None",
stack_path=self.stack_path,
),
]
result = parser.get_routes()

self.assertEqual(expected, result)

@parameterized.expand(
[
param("empty swagger", {}),
Expand Down
Loading