Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
a68acba
feat: add get att intrinsic function
Luis-Henrique Mar 28, 2024
7dddd14
Merge branch 'main' into feat/getatt-intrinsic-function
Luis-Henrique Mar 28, 2024
96b1b45
feat: add join intrinsic function
Luis-Henrique Mar 28, 2024
021100a
feat: add join intrinsic function
Luis-Henrique Mar 28, 2024
e4468e7
docs: update docstrings
Luis-Henrique Mar 28, 2024
8b802b0
feat: add get att intrinsic function
Luis-Henrique Mar 28, 2024
6fd85c2
feat: add select intrinsic function
Luis-Henrique Mar 28, 2024
dcf019a
fix: resolve intrinsic functions in select
Luis-Henrique Apr 1, 2024
cecc57f
feat: add split intrinsic function
Luis-Henrique Apr 1, 2024
c97fb1c
feat: add split intrinsic function
Luis-Henrique Apr 1, 2024
ac26009
Merge branch 'feat/getatt-intrinsic-function' into feat/join-intrinsi…
Luis-Henrique Apr 3, 2024
f74aad5
Merge branch 'feat/join-intrinsic-function' into feat/select-intrinsi…
Luis-Henrique Apr 3, 2024
42a44c6
Merge branch 'feat/select-intrinsic-function' into feat/add-split-fun…
Luis-Henrique Apr 3, 2024
bb44bbf
feat: update intrinsic function to handle the short form of get att
Luis-Henrique Apr 14, 2024
4fae8ca
Merge branch 'main' into feat/getatt-intrinsic-function
Luis-Henrique Apr 14, 2024
027258f
Merge branch 'feat/getatt-intrinsic-function' into feat/join-intrinsi…
Luis-Henrique Apr 15, 2024
d257d56
Merge branch 'feat/join-intrinsic-function' into feat/select-intrinsi…
Luis-Henrique Apr 15, 2024
2a1b04b
Merge branch 'feat/select-intrinsic-function' into feat/add-split-fun…
Luis-Henrique Apr 15, 2024
aa2b1db
feat: update select function
Luis-Henrique Apr 15, 2024
8a955b0
Merge branch 'main' into feat/getatt-intrinsic-function
Luis-Henrique Apr 15, 2024
26ad31f
Merge branch 'feat/getatt-intrinsic-function' into feat/join-intrinsi…
Luis-Henrique Apr 15, 2024
8fbb012
Merge branch 'feat/join-intrinsic-function' into feat/select-intrinsi…
Luis-Henrique Apr 15, 2024
b9c9903
Merge branch 'feat/select-intrinsic-function' into feat/add-split-fun…
Luis-Henrique Apr 15, 2024
9c2d633
wip: add sub function
Luis-Henrique Apr 15, 2024
9988540
wip: add sub function
Luis-Henrique Apr 15, 2024
c8d33da
refactor: update param type and variable name
Luis-Henrique Apr 15, 2024
f4ca1bf
Merge branch 'feat/getatt-intrinsic-function' of github.com:noverde/f…
Luis-Henrique Apr 15, 2024
02e7cd9
feat: update join function
Luis-Henrique Apr 15, 2024
20099c1
Merge branch 'feat/join-intrinsic-function' of github.com:noverde/fas…
Luis-Henrique Apr 15, 2024
312b9fa
feat: update join function
Luis-Henrique Apr 15, 2024
fcc3a63
Merge branch 'feat/getatt-intrinsic-function' into feat/join-intrinsi…
Luis-Henrique Apr 15, 2024
f244a86
feat: update select function test
Luis-Henrique Apr 15, 2024
7e1a3ce
Merge branch 'feat/select-intrinsic-function' of github.com:noverde/f…
Luis-Henrique Apr 15, 2024
83cdf16
Merge branch 'feat/join-intrinsic-function' into feat/select-intrinsi…
Luis-Henrique Apr 15, 2024
1b47c20
Merge branch 'feat/select-intrinsic-function' into feat/add-split-fun…
Luis-Henrique Apr 16, 2024
b3d14ce
feat: update select function
Luis-Henrique Apr 16, 2024
2258fd1
feat: update select function
Luis-Henrique Apr 16, 2024
8dd68d5
Merge branch 'feat/select-intrinsic-function' into feat/add-split-fun…
Luis-Henrique Apr 16, 2024
078ab66
feat: update split function
Luis-Henrique Apr 16, 2024
e810204
feat: update split function
Luis-Henrique Apr 16, 2024
3a25222
test: add getatt function tests
Luis-Henrique Apr 16, 2024
4acd284
Merge branch 'feat/getatt-intrinsic-function' into feat/join-intrinsi…
Luis-Henrique Apr 16, 2024
0621aa3
test: add join function tests
Luis-Henrique Apr 16, 2024
8a07fd5
Merge branch 'feat/join-intrinsic-function' into feat/select-intrinsi…
Luis-Henrique Apr 16, 2024
901ac5f
test: add select function tests
Luis-Henrique Apr 16, 2024
3ee4c2a
Merge branch 'feat/select-intrinsic-function' into feat/add-split-fun…
Luis-Henrique Apr 16, 2024
350ab84
test: add split function tests
Luis-Henrique Apr 16, 2024
6b1f717
feat: make format
Luis-Henrique Apr 16, 2024
fc2eee9
Merge branch 'feat/add-split-function' into feat/add-sub-function
Luis-Henrique Apr 16, 2024
9521938
feat: add sub intrinsic function
Luis-Henrique Apr 17, 2024
050e3cc
feat: add sub intrinsic function
Luis-Henrique Apr 17, 2024
a7777b2
feat: update cloudformation templates
Luis-Henrique Apr 17, 2024
f868bf4
Merge branch 'feat/getatt-intrinsic-function' into feat/join-intrinsi…
Luis-Henrique Apr 17, 2024
fc33617
Merge branch 'feat/join-intrinsic-function' into feat/select-intrinsi…
Luis-Henrique Apr 17, 2024
194fcf2
feat: remove file
Luis-Henrique Apr 17, 2024
e0f2397
Merge branch 'feat/select-intrinsic-function' into feat/add-split-fun…
Luis-Henrique Apr 17, 2024
35fb0f7
Merge branch 'feat/add-split-function' into feat/add-sub-function
Luis-Henrique Apr 17, 2024
a65a566
fix: update replace function
Luis-Henrique Apr 17, 2024
16cd406
Merge branch 'feat/add-sub-function' of github.com:noverde/faster-sam…
Luis-Henrique Apr 17, 2024
0cf27a0
feat: uodate replace function
Luis-Henrique Apr 17, 2024
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
258 changes: 257 additions & 1 deletion faster_sam/cloudformation.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import base64
import logging
from enum import Enum
import os
from pathlib import Path
import re
from typing import Any, Dict, List, Optional, Union

import yaml
Expand Down Expand Up @@ -584,7 +586,16 @@ def eval(function: Dict[str, Any], template: Dict[str, Any]) -> Any:
NotImplementedError
If the intrinsic function is not implemented.
"""
implemented = ("Fn::Base64", "Fn::FindInMap", "Ref")
implemented = (
"Fn::Base64",
"Fn::FindInMap",
"Fn::GetAtt",
"Fn::Join",
"Fn::Select",
"Fn::Split",
"Fn::Sub",
"Ref",
)

fun, val = list(function.items())[0]

Expand All @@ -597,6 +608,21 @@ def eval(function: Dict[str, Any], template: Dict[str, Any]) -> Any:
if "Fn::FindInMap" == fun:
return IntrinsicFunctions.find_in_map(val, template)

if "Fn::GetAtt" == fun:
return IntrinsicFunctions.get_att(val, template)

if "Fn::Join" == fun:
return IntrinsicFunctions.join(val, template)

if "Fn::Select" == fun:
return IntrinsicFunctions.select(val, template)

if "Fn::Split" == fun:
return IntrinsicFunctions.split(val, template)

if "Fn::Sub" == fun:
return IntrinsicFunctions.sub(val, template)

if "Ref" == fun:
return IntrinsicFunctions.ref(val, template)

Expand Down Expand Up @@ -676,3 +702,233 @@ def ref(value: str, template: Dict[str, Any]) -> Optional[str]:
# NOTE: this is a partial implementation

return None

@staticmethod
def get_att(value: Union[List[str], str], template: Dict[str, Any]) -> Optional[str]:
"""
Gets the value of an attribute from a CloudFormation template based on a list
of logical name and attribute name.

Parameters
----------
value : List[Any]
List containing the logical name and attribute name
template : Dict[str, Any]
A dictionary representing the CloudFormation template.

Returns
-------
Optional[str]
The value of atribute name, or None if the keys are not found.
"""
if isinstance(value, str):
value = value.split(".")

logical_name, attribute_name = value

if logical_name not in template["Resources"]:
return None

if isinstance(attribute_name, dict):
attribute_name = IntrinsicFunctions.eval(attribute_name, template)

if attribute_name is None:
return None

if attribute_name not in template["Resources"][logical_name]["Properties"]:
return None

attribute_value = template["Resources"][logical_name]["Properties"][attribute_name]

if isinstance(attribute_value, dict):
attribute_value = IntrinsicFunctions.eval(attribute_value, template)

if attribute_value is None:
return None

return attribute_value

@staticmethod
def join(value: List[Any], template: Dict[str, Any]) -> Optional[str]:
"""
Joins elements in a list with a specified delimiter.

Parameters
----------
value : List[Any]
A list containing two elements: the delimiter as the first element,
and the values to join as the second element.
template : Dict[str, Any]
A dictionary representing the CloudFormation template.

Returns
-------
Optional[str]
The joined string if successful; otherwise, None.
"""
delimiter, values = value

for index, element in enumerate(values):
if isinstance(element, dict):
element = IntrinsicFunctions.eval(element, template)

if element is None:
return None

values[index] = element

return delimiter.join(values)

@staticmethod
def select(value: List[Any], template: Dict[str, Any]) -> Optional[str]:
"""
Selects a value from a list based on the given index. If the value at the index
is a dictionary, it evaluates it using CloudFormation template data.
Parameters
----------
value : List[Any]
A list containing values from which to select.
template : Dict[str, Any]
A dictionary representing the CloudFormation template.
Returns
-------
Optional[str]
The selected value from the list, or None if any of the evaluated
values are None.
"""
index, objects = value

if isinstance(index, dict):
index = IntrinsicFunctions.eval(index, template)

if index is None:
return None

if isinstance(objects, dict):
objects = IntrinsicFunctions.eval(objects, template)

if objects is None:
return None
else:
for i, obj in enumerate(objects):
if isinstance(obj, dict):
objects[i] = IntrinsicFunctions.eval(obj, template)

if objects[i] is None:
return None

return objects[int(index)]

@staticmethod
def split(value: List[Any], template: Dict[str, Any]) -> Optional[str]:
"""
Splits a list of values using a specified delimiter.

Parameters
----------
value : List[Any]
A tuple containing the delimiter as its first element, followed
by a list of values to split.
template : Dict[str, Any]
A dictionary representing the CloudFormation template.

Returns
-------
Optional[str]
A list of strings resulting from splitting using the delimiter.
or None if any of the evaluated values are None.
"""
delimiter, source = value

if isinstance(source, dict):
source = IntrinsicFunctions.eval(source, template)

if source is None:
return None

return source.split(delimiter)

def replace_placeholders(string: str, matches: List[Any]):
pseudo_parameters = [
"AWS::AccountId",
"AWS::NotificationARNs",
"AWS::NoValue",
"AWS::Partition",
"AWS::Region",
"AWS::StackId",
"AWS::StackName",
"AWS::URLSuffix",
]

result_string = string

for match in matches:
if match in pseudo_parameters:
if match.replace("::", "_") in os.environ:
env_var = os.environ[match.replace("::", "_")]
result_string = result_string.replace(f"${{{match}}}", env_var)
else:
return None
else:
if match in os.environ:
env_var = os.environ[match]
result_string = result_string.replace(f"${{{match}}}", env_var)
else:
return None

return result_string

@staticmethod
def sub(value: List[Any], template: Dict[str, Any]) -> Optional[str]:
"""
Substitutes intrinsic functions and environment variables in the given value.

Parameters
----------
value : List[Any]
A list containing the string to perform substitutions on and a dictionary
containing variable names and their corresponding values.
template : Dict[str, Any]
A dictionary representing the CloudFormation template.

Returns
-------
Optional[str]
The resulting string after performing substitutions, or None if any of the
variables or intrinsic functions could not be resolved.
"""
pattern = r"\${(.*?)}"

if isinstance(value, list):
string, var_list = value

for var_dict in var_list:
var_name, var_value = list(var_dict.items())[0]

if isinstance(var_name, dict):
var_name = IntrinsicFunctions.eval(var_name, template)

if var_name is None:
return None

if isinstance(var_value, dict):
var_value = IntrinsicFunctions.eval(var_value, template)

if var_value is None:
return None

if var_name in string:
result = string.replace(f"${{{var_name}}}", str(var_value))
else:
return None

matches = re.findall(pattern, result)

if not matches:
return result

return IntrinsicFunctions.replace_placeholders(result, matches)
else:
matches = re.findall(pattern, value)

return IntrinsicFunctions.replace_placeholders(value, matches)
10 changes: 10 additions & 0 deletions tests/fixtures/templates/example2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Description: >
Parameters:
Environment:
Type: String
Handler:
type: String
Default: handler

Mappings:
Environments:
Expand All @@ -31,6 +34,13 @@ Globals:
- Environments
- Ref: Environment
- LogLevel
HANDLER:
Fn::Join:
- "."
- - fixtures
- handlers
- lambda_handler
- Ref: Handler

Resources:
ApiGateway:
Expand Down
24 changes: 23 additions & 1 deletion tests/fixtures/templates/example3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ Parameters:
Environment:
Type: String
Default: development
StageName:
Type: String
Default: v1
AttributeName:
Type: String
Default: StageName
Accounts:
Type: String
Default: dev@gmail|dotz@gmail.com

Mappings:
Environments:
Expand All @@ -29,7 +38,10 @@ Resources:
Type: AWS::Serverless::Api
Properties:
Name: sam-api
StageName: v1
StageName:
Ref: StageName
Tags:
Ref: Tags
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Expand Down Expand Up @@ -63,3 +75,13 @@ Resources:
- Environments
- Ref: Environment
- LogLevel
STAGE_NAME:
Fn::GetAtt:
- ApiGateway
- Ref: AttributeName
SENDER_ACCOUNT:
Fn::Select:
- 1
- Fn::Split:
- "|"
- "dev@gmail|dotz@gmail.com"
25 changes: 25 additions & 0 deletions tests/fixtures/templates/example4.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,30 @@ Parameters:
Environment:
Type: String
Default: development
Handler:
Type: String
Default: handler2
HandlerList:
Type: String
Default: [handler, handler2]
Account:
Type: String
Default: 1234
VarName:
Type: String
Default: account

Mappings:
Environments:
development:
LogLevel: DEBUG
Index: 1
staging:
LogLevel: WARNING
Index: 1
production:
LogLevel: ERROR
Index: 1

Globals:
Function:
Expand All @@ -27,6 +42,16 @@ Globals:
Variables:
ENVIRONMENT:
Ref: Environment
STAGE_NAME:
Fn::GetAtt: ApiGateway.Region
HANDLER:
Fn::Select:
- Fn::FindInMap:
- Environments
- Ref: Environment
- Index
- - handler1
- Ref: Handler

Resources:
ApiGateway:
Expand Down
Loading