3 O4Â\ì^ã@sxddlZddlmZddlmZmZGdd„deƒZGdd„deƒZGdd „d eƒZ Gd d „d eƒZ Gd d „d eƒZ dS)éN)Ú string_types)ÚInvalidTemplateExceptionÚInvalidDocumentExceptionc@sLeZdZdZdZdZdd„Zdd„Zdd „Zd d „Z d d „Z e dd„ƒZ dS)ÚActionaN Base class for intrinsic function actions. Each intrinsic function must subclass this, override the intrinsic_name, and provide a resolve() method Subclasses would be working on the JSON representation of an intrinsic function like {Ref: foo} than the YAML version !Ref foo because the input is already in JSON. Ú.NcCs|jstdƒ‚dS)Nz&Subclass must provide a intrinsic_name)Úintrinsic_nameÚ TypeError)Úself©r úˆ/Users/sshvans/Documents/Work/Quickstart/python-virtual-environments/env/lib/python3.6/site-packages/samtranslator/intrinsics/actions.pyÚ__init__szAction.__init__cCs tdƒ‚dS)zW Subclass must implement this method to resolve the intrinsic function z#Subclass must implement this methodN)ÚNotImplementedError)r Ú input_dictÚ parametersr r r Úresolve_parameter_refsszAction.resolve_parameter_refscCs tdƒ‚dS)zT Subclass must implement this method to resolve resource references z#Subclass must implement this methodN)r )r rÚsupported_resource_refsr r r Úresolve_resource_refsszAction.resolve_resource_refscCs tdƒ‚dS)zT Subclass must implement this method to resolve resource references z#Subclass must implement this methodN)r )r rÚsupported_resource_id_refsr r r Úresolve_resource_id_refs#szAction.resolve_resource_id_refscCs(|dk o&t|tƒo&t|ƒdko&|j|kS)a Validates that the input dictionary contains only one key and is of the given intrinsic_name :param input_dict: Input dictionary representing the intrinsic function :return: True if it matches expected structure, False otherwise Né)Ú isinstanceÚdictÚlenr)r rr r r Ú can_handle)s  zAction.can_handlecCsJd}t|tƒs|S|j|jdƒ}t|ƒdks6t|ƒ r:|S|d|dfS)a³ Splits a resource reference of structure "LogicalId.Property" and returns the "LogicalId" and "Property" separately. :param string ref_value: Input reference value which *may* contain the structure "LogicalId.Property" :return string, string: Returns two values - logical_id, property. If the input does not contain the structure, then both `logical_id` and property will be None Nrér)NN)rrÚsplitÚ_resource_ref_separatorrÚall)ÚclsÚ ref_valueZ no_resultÚsplitsr r r Ú_parse_resource_reference6s  z Action._parse_resource_reference) Ú__name__Ú __module__Ú __qualname__Ú__doc__rrr rrrrÚ classmethodr!r r r r rs rc@s(eZdZdZdd„Zdd„Zdd„ZdS) Ú RefActionZRefcCs>|j|ƒs|S||j}t|tƒs&|S||kr6||S|SdS)a£ Resolves references that are present in the parameters and returns the value. If it is not in parameters, this method simply returns the input unchanged. :param input_dict: Dictionary representing the Ref function. Must contain only one key and it should be "Ref". Ex: {Ref: "foo"} :param parameters: Dictionary of parameter values for resolution :return: N)rrrr)r rrÚ param_namer r r rRs   z RefAction.resolve_parameter_refscCsL|j|ƒs|S||j}|j|ƒ\}}|s.|S|j||ƒ}|sB|S|j|iS)aØ Resolves references to some property of a resource. These are runtime properties which can't be converted to a value here. Instead we output another reference that will more actually resolve to the value when executed via CloudFormation Example: {"Ref": "LogicalId.Property"} => {"Ref": "SomeOtherLogicalId"} :param dict input_dict: Dictionary representing the Ref function to be resolved. :param samtranslator.intrinsics.resource_refs.SupportedResourceReferences supported_resource_refs: Instance of an `SupportedResourceReferences` object that contain value of the property. :return dict: Dictionary with resource references resolved. )rrr!Úget)r rrrÚ logical_idÚpropertyÚresolved_valuer r r rjs   zRefAction.resolve_resource_refscCsR|j|ƒs|S||j}t|tƒ s.|j|kr2|S|}|j|ƒ}|sH|S|j|iS)a¸ Updates references to the old logical id of a resource to the new (generated) logical id. Example: {"Ref": "MyLayer"} => {"Ref": "MyLayerABC123"} :param dict input_dict: Dictionary representing the Ref function to be resolved. :param dict supported_resource_id_refs: Dictionary that maps old logical ids to new ones. :return dict: Dictionary with resource references resolved. )rrrrrr))r rrrr*r,r r r r‹s   z"RefAction.resolve_resource_id_refsN)r"r#r$rrrrr r r r r'Os!r'c@s@eZdZdZdd„Zdd„Zdd„Zdd „Zd d „Zd d „Z dS)Ú SubActionzFn::Subcs‡fdd„}|j||ƒS)af Substitute references found within the string of `Fn::Sub` intrinsic function :param input_dict: Dictionary representing the Fn::Sub function. Must contain only one key and it should be `Fn::Sub`. Ex: {"Fn::Sub": ...} :param parameters: Dictionary of parameter values for substitution :return: Resolved cs ˆj||ƒS)a] Replace parameter references with actual value. Return value of this method is directly replaces the reference structure :param full_ref: => ${logicalId.property} :param prop_name: => logicalId.property :return: Either the value it resolves to. If not the original reference )r))Úfull_refZ prop_name)rr r Údo_replacement·s z8SubAction.resolve_parameter_refs..do_replacement)Ú_handle_sub_action)r rrr/r )rr r¬s z SubAction.resolve_parameter_refscs‡‡fdd„}ˆj||ƒS)aP Resolves reference to some property of a resource. Inside string to be substituted, there could be either a "Ref" or a "GetAtt" usage of this property. They have to be handled differently. Ref usages are directly converted to a Ref on the resolved value. GetAtt usages are split under the assumption that there can be only one property of resource referenced here. Everything else is an attribute reference. Example: Let's say `LogicalId.Property` will be resolved to `ResolvedValue` Ref usage: ${LogicalId.Property} => ${ResolvedValue} GetAtt usage: ${LogicalId.Property.Arn} => ${ResolvedValue.Arn} ${LogicalId.Property.Attr1.Attr2} => {ResolvedValue.Attr1.Attr2} :param input_dict: Dictionary to be resolved :param samtranslator.intrinsics.resource_refs.SupportedResourceReferences supported_resource_refs: Instance of an `SupportedResourceReferences` object that contain value of the property. :return: Resolved dictionary cs\|jˆjƒ}t|ƒdkr|S|d}|d}ˆj||ƒ}|s@|Sˆjj||gƒ}|j||ƒS)a{ Perform the appropriate replacement to handle ${LogicalId.Property} type references inside a Sub. This method is called to get the replacement string for each reference within Sub's value :param full_ref: Entire reference string such as "${LogicalId.Property}" :param ref_value: Just the value of the reference such as "LogicalId.Property" :return: Resolved reference of the structure "${SomeOtherLogicalId}". Result should always include the ${} structure since we are not resolving to final value, but just converting one reference to another rrr)rrrr)ÚjoinÚreplace)r.rr r*r+r,Ú replacement)r rr r r/Þs   z7SubAction.resolve_resource_refs..do_replacement)r0)r rrr/r )r rr rÄszSubAction.resolve_resource_refscs‡‡fdd„}ˆj||ƒS)a» Resolves reference to some property of a resource. Inside string to be substituted, there could be either a "Ref" or a "GetAtt" usage of this property. They have to be handled differently. Ref usages are directly converted to a Ref on the resolved value. GetAtt usages are split under the assumption that there can be only one property of resource referenced here. Everything else is an attribute reference. Example: Let's say `LogicalId` will be resolved to `NewLogicalId` Ref usage: ${LogicalId} => ${NewLogicalId} GetAtt usage: ${LogicalId.Arn} => ${NewLogicalId.Arn} ${LogicalId.Attr1.Attr2} => {NewLogicalId.Attr1.Attr2} :param input_dict: Dictionary to be resolved :param dict supported_resource_id_refs: Dictionary that maps old logical ids to new ones. :return: Resolved dictionary csB|jˆjƒ}t|ƒdkr|S|d}ˆj|ƒ}|s6|S|j||ƒS)ar Perform the appropriate replacement to handle ${LogicalId} type references inside a Sub. This method is called to get the replacement string for each reference within Sub's value :param full_ref: Entire reference string such as "${LogicalId.Property}" :param ref_value: Just the value of the reference such as "LogicalId.Property" :return: Resolved reference of the structure "${SomeOtherLogicalId}". Result should always include the ${} structure since we are not resolving to final value, but just converting one reference to another rr)rrrr)r2)r.rr r*r,)r rr r r/s   z:SubAction.resolve_resource_id_refs..do_replacement)r0)r rrr/r )r rr rÿsz"SubAction.resolve_resource_id_refscCs0|j|ƒs|S|j}||}|j||ƒ||<|S)aç Handles resolving replacements in the Sub action based on the handler that is passed as an input. :param input_dict: Dictionary to be resolved :param supported_values: One of several different objects that contain the supported values that need to be changed. See each method above for specifics on these objects. :param handler: handler that is specific to each implementation. :return: Resolved value of the Sub dictionary )rrÚ_handle_sub_value)r rÚhandlerÚkeyÚ sub_valuer r r r07s zSubAction._handle_sub_actioncCsTt|tƒr|j||ƒ}n8t|tƒrPt|ƒdkrPt|dtƒrP|j|d|ƒ|d<|S)aß Generic method to handle value to Fn::Sub key. We are interested in parsing the ${} syntaxes inside the string portion of the value. :param sub_value: Value of the Sub function :param handler_method: Method to be called on every occurrence of `${LogicalId}` structure within the string. Implementation could resolve and replace this structure with whatever they seem fit :return: Resolved value of the Sub dictionary r)rrÚ _sub_all_refsÚlistr)r r7Úhandler_methodr r r r4Ks $zSubAction._handle_sub_valuecs,d}tjd|dƒ}tj|‡fdd„|ƒS)ao Substitute references within a string that is using ${key} syntax by calling the `handler_method` on every occurrence of this structure. The value returned by this method directly replaces the reference structure. Ex: text = "${key1}-hello-${key2} def handler_method(full_ref, ref_value): return "foo" _sub_all_refs(text, handler_method) will output "foo-hello-foo" :param string text: Input text :param handler_method: Method to be called to handle each occurrence of ${blah} reference structure. First parameter to this method is the full reference structure Ex: ${LogicalId.Property}. Second parameter is just the value of the reference such as "LogicalId.Property" :return string: Text with all reference structures replaced as necessary z"[A-Za-z0-9\.]+|AWS::[A-Z][A-Za-z]*z\$\{(z)\}csˆ|jdƒ|jdƒƒS)Nrr)Úgroup)Úmatch)r:r r Úsz)SubAction._sub_all_refs..)ÚreÚcompileÚsub)r Útextr:Zlogical_id_regexZ ref_patternr )r:r r8bs  zSubAction._sub_all_refsN) r"r#r$rrrrr0r4r8r r r r r-©s;8r-c@s0eZdZdZdd„Zdd„Zdd„Zdd „Zd S) Ú GetAttActionz Fn::GetAttcCs|S)Nr )r rrr r r r†sz#GetAttAction.resolve_parameter_refsc Cs®|j|ƒs|S|j}||}t|tƒ s4t|ƒdkr8|Stdd„|Dƒƒs^ttdj|ƒƒgƒ‚|j j |ƒ}|j |j ƒ}|d}|d}|dd…} |j ||ƒ} |j ||| | ƒS)a‡ Resolve resource references within a GetAtt dict. Example: { "Fn::GetAtt": ["LogicalId.Property", "Arn"] } => {"Fn::GetAtt": ["ResolvedLogicalId", "Arn"]} Theoretically, only the first element of the array can contain reference to SAM resources. The second element is name of an attribute (like Arn) of the resource. However tools like AWS CLI apply the assumption that first element of the array is a LogicalId and cannot contain a 'dot'. So they break at the first dot to convert YAML tag to JSON map like this: `!GetAtt LogicalId.Property.Arn` => {"Fn::GetAtt": [ "LogicalId", "Property.Arn" ] } Therefore to resolve the reference, we join the array into a string, break it back up to check if it contains a known reference, and resolve it if we can. :param input_dict: Dictionary to be resolved :param samtransaltor.intrinsics.resource_refs.SupportedResourceReferences supported_resource_refs: Instance of an `SupportedResourceReferences` object that contain value of the property. :return: Resolved dictionary rcss|]}t|tƒVqdS)N)rr)Ú.0Úentryr r r ú ®sz5GetAttAction.resolve_resource_refs..z@Invalid GetAtt value {}. GetAtt expects an array with 2 strings.rrN)rrrr9rrrrÚformatrr1rr)Ú_get_resolved_dictionary) r rrr6ÚvalueÚ value_strr r*r+Ú remainingr,r r r rŠs"     z"GetAttAction.resolve_resource_refsc Cs~|j|ƒs|S|j}||}t|tƒ s4t|ƒdkr8|S|jj|ƒ}|j|jƒ}|d}|dd…}|j|ƒ} |j ||| |ƒS)aÿ Resolve resource references within a GetAtt dict. Example: { "Fn::GetAtt": ["LogicalId", "Arn"] } => {"Fn::GetAtt": ["ResolvedLogicalId", "Arn"]} Theoretically, only the first element of the array can contain reference to SAM resources. The second element is name of an attribute (like Arn) of the resource. However tools like AWS CLI apply the assumption that first element of the array is a LogicalId and cannot contain a 'dot'. So they break at the first dot to convert YAML tag to JSON map like this: `!GetAtt LogicalId.Arn` => {"Fn::GetAtt": [ "LogicalId", "Arn" ] } Therefore to resolve the reference, we join the array into a string, break it back up to check if it contains a known reference, and resolve it if we can. :param input_dict: Dictionary to be resolved :param dict supported_resource_id_refs: Dictionary that maps old logical ids to new ones. :return: Resolved dictionary rrrN) rrrr9rrr1rr)rG) r rrr6rHrIr r*rJr,r r r rÆs     z%GetAttAction.resolve_resource_id_refscCs|r|g|||<|S)a: Resolves the function and returns the updated dictionary :param input_dict: Dictionary to be resolved :param key: Name of this intrinsic. :param resolved_value: Resolved or updated value for this action. :param remaining: Remaining sections for the GetAtt action. r )r rr6r,rJr r r rGñs z%GetAttAction._get_resolved_dictionaryN)r"r#r$rrrrrGr r r r rBƒs <+rBc@seZdZdZdZdd„ZdS)ÚFindInMapActionz= This action can't be used along with other actions. z Fn::FindInMapcCsÔ|j|ƒs|S||j}t|tƒ s0t|ƒdkrDttdj|ƒƒgƒ‚|j|d|ƒ}|j|d|ƒ}|j|d|ƒ}t|t ƒ s˜t|t ƒ s˜t|t ƒ rœ|S||ksÀ|||ksÀ||||krÄ|S||||S)a¶ Recursively resolves "Fn::FindInMap"references that are present in the mappings and returns the value. If it is not in mappings, this method simply returns the input unchanged. :param input_dict: Dictionary representing the FindInMap function. Must contain only one key and it should be "Fn::FindInMap". :param parameters: Dictionary of mappings from the SAM template ézEInvalid FindInMap value {}. FindInMap expects an array with 3 values.rrr) rrrr9rrrrFrr)r rrrHZmap_nameZ top_level_keyZsecond_level_keyr r r rs&       z&FindInMapAction.resolve_parameter_refsN)r"r#r$r%rrr r r r rKsrK) r>ÚsixrZsamtranslator.model.exceptionsrrÚobjectrr'r-rBrKr r r r Ús HZ[