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

(feat) CloudFormation AWS::ApiGateway::Methods and AWS::ApiGateway::Resources support #1247

Merged
merged 78 commits into from
Aug 12, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
8f6b8fb
Initial setup and work
viksrivat Jun 17, 2019
f457401
Add RestApi tests
viksrivat Jun 19, 2019
8112cf4
Remove Stage Name/Variable from pr
viksrivat Jun 20, 2019
5050dd8
Run make pr
viksrivat Jun 20, 2019
bf5a083
Add Rest Api integration test
viksrivat Jun 20, 2019
6eb226a
Remove AWS::Stage from current pr
viksrivat Jun 20, 2019
7c5823d
Update integration test to only use CloudFormation types
viksrivat Jun 20, 2019
4966513
Re-run Travis Ci
viksrivat Jun 24, 2019
2c38ede
Update Uri to BodyS3Location
viksrivat Jun 24, 2019
9d3d137
Add docstring to extract_swagger_api
viksrivat Jun 24, 2019
b6d9b62
Fix Merge conflicts with dev and branch
viksrivat Jun 24, 2019
f8ac623
Update AWS::ApiGateway::Stage tests
viksrivat Jun 24, 2019
54bda7f
Test local file in body fail and clean tests
viksrivat Jun 24, 2019
9b06696
Initial Attempt
viksrivat Jun 25, 2019
145162e
Refactor Sam API provider
viksrivat Jun 25, 2019
5615163
Cleanup Refactoring
viksrivat Jun 25, 2019
752fba8
Merge pull request #1 from viksrivat/feature/cloud_formation_api_prov…
viksrivat Jun 25, 2019
1157284
Update AWS::ApiGateway::Stage tests
viksrivat Jun 24, 2019
5d1dfc7
Fix merge conflict
viksrivat Jun 25, 2019
7ed267c
Remove .vscode
viksrivat Jun 25, 2019
681ede3
Re-run travis
viksrivat Jun 25, 2019
a0f5837
Restructure the information Api has the way it passed through
viksrivat Jun 26, 2019
a3ca29f
Merge pull request #2 from viksrivat/cleanup/update_common_stage_feat…
viksrivat Jun 26, 2019
0078786
Update common
viksrivat Jun 26, 2019
9879105
Merge branch 'feature/cloud_formation_stage_support' of github.com:vi…
viksrivat Jun 26, 2019
a9117ad
Update AbstractParserProvider documentation
viksrivat Jun 26, 2019
997845c
Initial Refactor refactor
viksrivat Jun 26, 2019
de65fd0
Fix tests and run make pr
viksrivat Jun 26, 2019
df243a7
Fix merge conflict
viksrivat Jun 27, 2019
4555f11
Reorganize classes
viksrivat Jun 27, 2019
420a842
Update docstrings
viksrivat Jun 27, 2019
53f4b7e
Update the SAMBaseProvider
viksrivat Jun 27, 2019
856326e
Fix merge conflict
viksrivat Jun 27, 2019
6803a0b
Restructure to seperate SAM and CF providers seperately + tests
viksrivat Jun 27, 2019
837137f
Remove transform flag in the rest-api
viksrivat Jun 27, 2019
4130ea2
Remove BaseProvider object
viksrivat Jun 27, 2019
ec833dc
Fix merge conflict
viksrivat Jun 27, 2019
693692b
Update with style
viksrivat Jun 27, 2019
19eaebe
Update tests and code with comments
viksrivat Jun 27, 2019
d226f53
Fix merge conflicts with comments from feature/cfn_rest_api
viksrivat Jun 27, 2019
23467de
Update tests with comments
viksrivat Jun 28, 2019
7e25c02
Fix merge conflict tests
viksrivat Jun 28, 2019
066b236
Trigger
viksrivat Jun 28, 2019
36018f1
Start working on method&resources
viksrivat Jun 28, 2019
8987db2
Remove noapievent
viksrivat Jun 28, 2019
626ea71
Update cloud_formation resource processing
viksrivat Jun 28, 2019
666b2e4
Update cfn_api_provider
viksrivat Jun 28, 2019
4a792bf
Update tests
viksrivat Jun 28, 2019
644b851
Update merge conflictz
viksrivat Jun 28, 2019
efdc3a4
Merge changes from stage
viksrivat Jun 28, 2019
fd4ab05
Add tests for resources&methods
viksrivat Jun 28, 2019
238742a
Fix bug with api
viksrivat Jun 29, 2019
739e74b
Update tests with make pr
viksrivat Jun 29, 2019
1d2e2b1
Fix tests and binary_types
viksrivat Jun 29, 2019
78341e9
Fix pathpart code
viksrivat Jun 29, 2019
2d0da34
Add cdk templates
viksrivat Jun 29, 2019
1c29946
Add serverless template
viksrivat Jun 29, 2019
7662f24
Fixed comments
viksrivat Jul 8, 2019
3df93ba
Update tests
viksrivat Jul 9, 2019
cc21095
Fix merge with rest_api
viksrivat Jul 9, 2019
b50e644
Remove Invalid Integration Test
viksrivat Jul 9, 2019
a01aee2
Run make pr
viksrivat Jul 9, 2019
8d2abd0
Fix merge conflict with stage
viksrivat Jul 9, 2019
49d16b4
feat(start-api): CloudFormation AWS::ApiGateway::RestApi support (#1238)
viksrivat Jul 23, 2019
aeba546
feat(start-api): CloudFormation AWS::ApiGateway::Stage Support (#1239)
viksrivat Jul 26, 2019
07830b9
Merge branch 'start-api/cfn' into feature/cloud_formation_method_reso…
viksrivat Jul 29, 2019
fa2472d
Fix merge issue with cfn_api_provider
viksrivat Jul 29, 2019
b594aed
Update tests
viksrivat Jul 29, 2019
3d79b5c
Fix Integration Tests
viksrivat Jul 29, 2019
45e7e51
Fix Integration Tests
viksrivat Jul 29, 2019
d751f00
Merge branch 'start-api/cfn' into feature/cloud_formation_method_reso…
viksrivat Aug 8, 2019
0bb1d3b
Update with comments
viksrivat Aug 9, 2019
33be359
Update method docstring
viksrivat Aug 9, 2019
b9b7396
Fix style for flake8
viksrivat Aug 9, 2019
fe2a2bb
Merge branch 'develop' into feature/cloud_formation_method_resources
jfuss Aug 9, 2019
fa3006d
Merge branch 'develop' into feature/cloud_formation_method_resources
jfuss Aug 10, 2019
7365d2c
Update intrinsic Test by fixing overriden yaml values
viksrivat Aug 12, 2019
2e1b4e1
Merge branch 'develop' into feature/cloud_formation_method_resources
jfuss Aug 12, 2019
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
Prev Previous commit
Next Next commit
Initial Attempt
  • Loading branch information
viksrivat committed Jun 25, 2019
commit 9b066968bf92539f5a614cc14f6b21de7d550685
245 changes: 245 additions & 0 deletions samcli/commands/local/lib/api_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
from commands.local.lib.provider import ApiProvider
from commands.local.lib.sam_api_provider import ApiCollector, SamApiProvider
from commands.local.lib.sam_base_provider import SamBaseProvider, LOG
from commands.local.lib.swagger.parser import SwaggerParser
from commands.local.lib.swagger.reader import SamSwaggerReader


class AbstractApiProvider(ApiProvider):
_IMPLICIT_API_RESOURCE_ID = "ServerlessRestApi"
_SERVERLESS_FUNCTION = "AWS::Serverless::Function"
_SERVERLESS_API = "AWS::Serverless::Api"
_GATEWAY_REST_API = "AWS::ApiGateway::RestApi"
_TYPE = "Type"

_FUNCTION_EVENT_TYPE_API = "Api"
_FUNCTION_EVENT = "Events"
_EVENT_PATH = "Path"
_EVENT_METHOD = "Method"

_ANY_HTTP_METHODS = ["GET",
"DELETE",
"PUT",
"POST",
"HEAD",
"OPTIONS",
"PATCH"]
PROVIDER_TYPE_CF = "CF"

def __init__(self, template_dict, api_provider=SamBaseProvider, parameter_overrides=None, cwd=None,
provider_type="CF"):
"""
Initialize the class with SAM template data. The template_dict (SAM Templated) is assumed
to be valid, normalized and a dictionary. template_dict should be normalized by running any and all
pre-processing before passing to this class.
This class does not perform any syntactic validation of the template.

After the class is initialized, changes to ``template_dict`` will not be reflected in here.
You will need to explicitly update the class with new template, if necessary.

Parameters
----------
template_dict : dict
SAM Template as a dictionary
cwd : str
Optional working directory with respect to which we will resolve relative path to Swagger file
"""
self.provider_type = provider_type
if self.provider_type == AbstractApiProvider.PROVIDER_TYPE_CF:
self.template_dict = api_provider.get_template(template_dict, parameter_overrides)
self.resources = self.template_dict.get("Resources", {})

LOG.debug("%d resources found in the template", len(self.resources))

# Store a set of apis
self.cwd = cwd
self.apis = self._extract_apis(self.resources)

LOG.debug("%d APIs found in the template", len(self.apis))

def get_all(self):
"""
Yields all the Lambda functions with Api Events available in the SAM Template.

:yields Api: namedtuple containing the Api information
"""

for api in self.apis:
yield api

def _extract_apis(self, resources):
"""
Extract all Implicit Apis (Apis defined through Serverless Function with an Api Event

:param dict resources: Dictionary of SAM/CloudFormation resources
:return: List of nametuple Api
"""

# Some properties like BinaryMediaTypes, Cors are set once on the resource but need to be applied to each API.
# For Implicit APIs, which are defined on the Function resource, these properties
# are defined on a AWS::Serverless::Api resource with logical ID "ServerlessRestApi". Therefore, no matter
# if it is an implicit API or an explicit API, there is a corresponding resource of type AWS::Serverless::Api
# that contains these additional configurations.
#
# We use this assumption in the following loop to collect information from resources of type
# AWS::Serverless::Api. We also extract API from Serverless::Function resource and add them to the
# corresponding Serverless::Api resource. This is all done using the ``collector``.

collector = ApiCollector()
providers = {provider.RESOURCE_TYPE: provider for provider in ParserApiProvider.__subclasses__() if
provider.PROVIDER_TYPE == self.provider_type}
for logical_id, resource in resources.items():
resource_type = resource.get(SamApiProvider._TYPE)
provider = providers.get(resource_type)
if provider:
provider.extract_api(logical_id, resource, collector)
apis = SamApiProvider._merge_apis(collector)
return self._normalize_apis(apis)


class ParserApiProvider(object):

def extract_api(self, logical_id, api_resource, collector, cwd=None):
pass

def get_type(self):
pass

def _extract_swagger_api(self, logical_id, body, uri, binary_media, collector, cwd=None):
"""
Parse the Swagger documents given the Api properties.

Parameters
----------
logical_id : str
Logical ID of the resource

body : dict
The body of the RestApi

uri : str or dict
The url to location of the RestApi

binary_media: list
The link to the binary media

collector: ApiCollector
Instance of the API collector that where we will save the API information
"""
reader = SamSwaggerReader(definition_body=body,
definition_uri=uri,
working_dir=cwd)
swagger = reader.read()
parser = SwaggerParser(swagger)
apis = parser.get_apis()
LOG.debug("Found '%s' APIs in resource '%s'", len(apis), logical_id)

collector.add_apis(logical_id, apis)
collector.add_binary_media_types(logical_id, parser.get_binary_media_types()) # Binary media from swagger
collector.add_binary_media_types(logical_id, binary_media) # Binary media specified on resource in template


class FunctionProvider(ParserApiProvider):
RESOURCE_TYPE = "AWS::Serverless::Function"
PROVIDER_TYPE = AbstractApiProvider.PROVIDER_TYPE_CF

def extract_api(self, logical_id, api_resource, collector, cwd=None):
return self._extract_apis_from_function(logical_id, api_resource, collector)

@staticmethod
def _extract_apis_from_function(logical_id, function_resource, collector):
"""
Fetches a list of APIs configured for this SAM Function resource.

Parameters
----------
logical_id : str
Logical ID of the resource

function_resource : dict
Contents of the function resource including its properties

collector : ApiCollector
Instance of the API collector that where we will save the API information
"""

resource_properties = function_resource.get("Properties", {})
serverless_function_events = resource_properties.get(SamApiProvider._FUNCTION_EVENT, {})
SamApiProvider._extract_apis_from_events(logical_id, serverless_function_events, collector)


class SAMApiProvider(ParserApiProvider):
RESOURCE_TYPE = "AWS::Serverless::Api"
PROVIDER_TYPE = AbstractApiProvider.PROVIDER_TYPE_CF

def extract_api(self, logical_id, api_resource, collector, cwd=None):
return self._extract_from_serverless_api(logical_id, api_resource, collector, cwd)

def _extract_from_serverless_api(self, logical_id, api_resource, collector, cwd=None):
"""
Extract APIs from AWS::Serverless::Api resource by reading and parsing Swagger documents. The result is added
to the collector.

Parameters
----------
logical_id : str
Logical ID of the resource

api_resource : dict
Resource definition, including its properties

collector : ApiCollector
Instance of the API collector that where we will save the API information
"""

properties = api_resource.get("Properties", {})
body = properties.get("DefinitionBody")
uri = properties.get("DefinitionUri")
binary_media = properties.get("BinaryMediaTypes", [])
stage_name = properties.get("StageName")
stage_variables = properties.get("Variables")

if not body and not uri:
# Swagger is not found anywhere.
LOG.debug("Skipping resource '%s'. Swagger document not found in DefinitionBody and DefinitionUri",
logical_id)
return
self._extract_swagger_api(logical_id, body, uri, binary_media, collector, cwd)
collector.add_stage_name(logical_id, stage_name)
collector.add_stage_variables(logical_id, stage_variables)


class CFNApiProvider(ParserApiProvider):
RESOURCE_TYPE = "AWS::ApiGateway::RestApi"
PROVIDER_TYPE = AbstractApiProvider.PROVIDER_TYPE_CF

def extract_api(self, logical_id, api_resource, collector, cwd=None):
return self._extract_cloud_formation_api(logical_id, api_resource, collector, cwd)

def _extract_cloud_formation_api(self, logical_id, api_resource, collector, cwd=None):
"""
Extract APIs from AWS::ApiGateway::RestApi resource by reading and parsing Swagger documents. The result is
added to the collector.

Parameters
----------
logical_id : str
Logical ID of the resource

api_resource : dict
Resource definition, including its properties

collector : ApiCollector
Instance of the API collector that where we will save the API information
"""
properties = api_resource.get("Properties", {})
body = properties.get("Body")
s3_location = properties.get("BodyS3Location")
binary_media = properties.get("BinaryMediaTypes", [])

if not body and not s3_location:
# Swagger is not found anywhere.
LOG.debug("Skipping resource '%s'. Swagger document not found in Body and BodyS3Location",
logical_id)
return
self._extract_swagger_api(logical_id, body, s3_location, binary_media, collector, cwd)
19 changes: 16 additions & 3 deletions samcli/commands/local/lib/sam_base_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,24 @@
from samcli.lib.samlib.wrapper import SamTranslatorWrapper
from samcli.lib.samlib.resource_metadata_normalizer import ResourceMetadataNormalizer


LOG = logging.getLogger(__name__)


class SamBaseProvider(object):
class BaseProvider(object):
@staticmethod
def get_template(template_dict, parameter_overrides=None):
pass

@staticmethod
def _get_parameter_values(template_dict, parameter_overrides):
pass

@staticmethod
def _get_default_parameter_values(sam_template):
pass


class SamBaseProvider(BaseProvider):
"""
Base class for SAM Template providers
"""
Expand Down Expand Up @@ -89,7 +102,7 @@ def _resolve_parameters(template_dict, parameter_overrides):
supported_intrinsics = {action.intrinsic_name: action() for action in SamBaseProvider._SUPPORTED_INTRINSICS}

# Intrinsics resolver will mutate the original template
return IntrinsicsResolver(parameters=parameter_values, supported_intrinsics=supported_intrinsics)\
return IntrinsicsResolver(parameters=parameter_values, supported_intrinsics=supported_intrinsics) \
.resolve_parameter_refs(template_dict)

@staticmethod
Expand Down