Skip to content
Open
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
6 changes: 6 additions & 0 deletions samtranslator/model/api/api_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ def __init__( # noqa: PLR0913
policy: Union[dict[str, Any], Intrinsicable[str]] | None = None,
security_policy: Intrinsicable[str] | None = None,
endpoint_access_mode: Intrinsicable[str] | None = None,
resolved_variables: dict[str, Any] | None = None,
):
"""Constructs an API Generator class that generates API Gateway resources

Expand Down Expand Up @@ -283,6 +284,10 @@ def __init__( # noqa: PLR0913
self.policy = policy
self.security_policy = security_policy
self.endpoint_access_mode = endpoint_access_mode
# Same stage variables as `variables`, but with parameter references resolved. Used only to hash the
# deployment logical id; the stage itself keeps `variables` so the emitted template preserves the
# customer's intrinsics. Falls back to `variables` when the caller did not resolve them.
self.resolved_variables = resolved_variables if resolved_variables is not None else variables

def _construct_rest_api(self) -> ApiGatewayRestApi: # noqa: PLR0912
"""Constructs and returns the ApiGateway RestApi.
Expand Down Expand Up @@ -474,6 +479,7 @@ def _construct_stage(
self.domain,
redeploy_restapi_parameters,
self.always_deploy,
stage_variables=self.resolved_variables,
)

if self.tags is not None:
Expand Down
17 changes: 16 additions & 1 deletion samtranslator/model/apigateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,16 @@ class ApiGatewayDeployment(Resource):

runtime_attrs = {"deployment_id": lambda self: ref(self.logical_id)}

def make_auto_deployable(
def make_auto_deployable( # noqa: PLR0913
self,
stage: ApiGatewayStage,
openapi_version: Union[dict[str, Any], str] | None = None,
swagger: dict[str, Any] | None = None,
domain: dict[str, Any] | None = None,
redeploy_restapi_parameters: Any | None = None,
always_deploy: bool | None = False,
*,
stage_variables: dict[str, Any] | None = None,
) -> None:
"""
Sets up the resource such that it will trigger a re-deployment when Swagger changes or always_deploy is true
Expand All @@ -116,6 +118,9 @@ def make_auto_deployable(
:param openapi_version: string containing value of OpenApiVersion flag in the template
:param domain: Dictionary containing the custom domain configuration for the API
:param redeploy_restapi_parameters: Dictionary containing the properties for which rest api will be redeployed
:param stage_variables: Stage variables with parameter references already resolved, for hashing only. Callers
must resolve them, because an unresolved {"Ref": "SomeParameter"} hashes identically for every parameter
value. Falls back to the stage's own (possibly unresolved) variables when not supplied.
"""
if not swagger:
return
Expand All @@ -131,6 +136,16 @@ def make_auto_deployable(
hash_input.append(str(openapi_version))
if domain:
hash_input.append(json.dumps(domain))
# Stage variables are applied with an UpdateStage call, which points the stage back at the
# deployment it already references. If they are not part of this hash, a variables-only
# change reuses the existing deployment: the new variables are never deployed, and any
# deployment made outside SAM since the last SAM deployment is reverted.
# Only added when set, so templates without stage variables keep their existing hash.
# `stage_variables` is the caller-resolved form; the stage's own value is the fallback and may
# still hold intrinsics, which hash identically across parameter values.
stage_variables = stage_variables if stage_variables is not None else getattr(stage, "Variables", None)
if stage_variables:
hash_input.append(json.dumps(stage_variables, sort_keys=True))
function_names = redeploy_restapi_parameters.get("function_names") if redeploy_restapi_parameters else None
# The deployment logical id is <api logicalId> + "Deployment"
# The keyword "Deployment" is removed and all the function names associated with api is obtained
Expand Down
9 changes: 9 additions & 0 deletions samtranslator/model/sam_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -1844,6 +1844,14 @@ def to_cloudformation(self, **kwargs) -> list[Resource]: # type: ignore[no-unty
self.BinaryMediaTypes = intrinsics_resolver.resolve_parameter_refs(self.BinaryMediaTypes)
self.Domain = intrinsics_resolver.resolve_parameter_refs(self.Domain)
self.Auth = intrinsics_resolver.resolve_parameter_refs(self.Auth)
# The deployment logical id is hashed from the stage variables, so parameter references have to be
# resolved for that hash to change when the deployed value changes: an unresolved {"Ref": "SomeParameter"}
# is byte-identical for every parameter value, which would leave the variables-only change undeployed
# (see ApiGatewayDeployment.make_auto_deployable). Unlike Domain above, this is deliberately NOT assigned
# back to self.Variables: resolve_parameter_refs mutates its argument and inlines the values, and the
# emitted AWS::ApiGateway::Stage must keep the customer's intrinsics. Hence a deep copy, used for the
# hash only.
resolved_variables = intrinsics_resolver.resolve_parameter_refs(copy.deepcopy(self.Variables))
redeploy_restapi_parameters = kwargs.get("redeploy_restapi_parameters")
shared_api_usage_plan = kwargs.get("shared_api_usage_plan")
template_conditions = kwargs.get("conditions")
Expand Down Expand Up @@ -1889,6 +1897,7 @@ def to_cloudformation(self, **kwargs) -> list[Resource]: # type: ignore[no-unty
policy=self.Policy,
security_policy=self.SecurityPolicy,
endpoint_access_mode=self.EndpointAccessMode,
resolved_variables=resolved_variables,
)

generated_resources = api_generator.to_cloudformation(redeploy_restapi_parameters, route53_record_set_groups)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ExplicitApiDeployment9252467a1e": {
"ExplicitApiDeploymente24cb02185": {
"Properties": {
"Description": "RestApi deployment id: 9252467a1edc49ba35cb258640f5e3734cc9fab1",
"Description": "RestApi deployment id: e24cb0218583daf7c5e537a4e639dfb3a2971fdf",
"RestApiId": {
"Ref": "ExplicitApi"
},
Expand All @@ -70,7 +70,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ExplicitApiDeployment9252467a1e"
"Ref": "ExplicitApiDeploymente24cb02185"
},
"RestApiId": {
"Ref": "ExplicitApi"
Expand Down Expand Up @@ -230,9 +230,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ServerlessRestApiDeploymentdb4b9da82a": {
"ServerlessRestApiDeployment62318f9715": {
"Properties": {
"Description": "RestApi deployment id: db4b9da82adc6031fcd32bf3a4954485464fc009",
"Description": "RestApi deployment id: 62318f9715b2b5932b597575f6a4a0eafd0cf7e3",
"RestApiId": {
"Ref": "ServerlessRestApi"
},
Expand All @@ -245,7 +245,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ServerlessRestApiDeploymentdb4b9da82a"
"Ref": "ServerlessRestApiDeployment62318f9715"
},
"RestApiId": {
"Ref": "ServerlessRestApi"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ExplicitApiDeployment7c4f7dda23": {
"ExplicitApiDeployment8e20079165": {
"Properties": {
"Description": "RestApi deployment id: 7c4f7dda23acd71e4a653861510d82ad7809e562",
"Description": "RestApi deployment id: 8e20079165a6421b14f28fc3f812a282e9ca3d90",
"RestApiId": {
"Ref": "ExplicitApi"
},
Expand All @@ -69,7 +69,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ExplicitApiDeployment7c4f7dda23"
"Ref": "ExplicitApiDeployment8e20079165"
},
"RestApiId": {
"Ref": "ExplicitApi"
Expand Down Expand Up @@ -267,9 +267,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ServerlessRestApiDeploymente091b9b9a0": {
"ServerlessRestApiDeployment26fa62bf10": {
"Properties": {
"Description": "RestApi deployment id: e091b9b9a0d7b8b9db6fee8c4ad295eb98edde08",
"Description": "RestApi deployment id: 26fa62bf10190a228c478ca0189e786b42e35acd",
"RestApiId": {
"Ref": "ServerlessRestApi"
},
Expand All @@ -282,7 +282,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ServerlessRestApiDeploymente091b9b9a0"
"Ref": "ServerlessRestApiDeployment26fa62bf10"
},
"RestApiId": {
"Ref": "ServerlessRestApi"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ExplicitApiDeployment9252467a1e": {
"ExplicitApiDeploymente24cb02185": {
"Properties": {
"Description": "RestApi deployment id: 9252467a1edc49ba35cb258640f5e3734cc9fab1",
"Description": "RestApi deployment id: e24cb0218583daf7c5e537a4e639dfb3a2971fdf",
"RestApiId": {
"Ref": "ExplicitApi"
},
Expand All @@ -78,7 +78,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ExplicitApiDeployment9252467a1e"
"Ref": "ExplicitApiDeploymente24cb02185"
},
"RestApiId": {
"Ref": "ExplicitApi"
Expand Down Expand Up @@ -246,9 +246,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ServerlessRestApiDeploymente1212668e0": {
"ServerlessRestApiDeployment409ed00828": {
"Properties": {
"Description": "RestApi deployment id: e1212668e096994ab32167666f5a877bd6ac5fad",
"Description": "RestApi deployment id: 409ed008280bb6489cdd1707ff7f2b66d5ad40bb",
"RestApiId": {
"Ref": "ServerlessRestApi"
},
Expand All @@ -261,7 +261,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ServerlessRestApiDeploymente1212668e0"
"Ref": "ServerlessRestApiDeployment409ed00828"
},
"RestApiId": {
"Ref": "ServerlessRestApi"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ExplicitApiDeployment7c4f7dda23": {
"ExplicitApiDeployment8e20079165": {
"Properties": {
"Description": "RestApi deployment id: 7c4f7dda23acd71e4a653861510d82ad7809e562",
"Description": "RestApi deployment id: 8e20079165a6421b14f28fc3f812a282e9ca3d90",
"RestApiId": {
"Ref": "ExplicitApi"
},
Expand All @@ -77,7 +77,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ExplicitApiDeployment7c4f7dda23"
"Ref": "ExplicitApiDeployment8e20079165"
},
"RestApiId": {
"Ref": "ExplicitApi"
Expand Down Expand Up @@ -283,9 +283,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ServerlessRestApiDeployment614ec93e15": {
"ServerlessRestApiDeploymente8a33c953e": {
"Properties": {
"Description": "RestApi deployment id: 614ec93e159f50797f73789c416660484070ee2e",
"Description": "RestApi deployment id: e8a33c953ef3abf412bc61a49eecc59a9eb71bc8",
"RestApiId": {
"Ref": "ServerlessRestApi"
},
Expand All @@ -298,7 +298,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ServerlessRestApiDeployment614ec93e15"
"Ref": "ServerlessRestApiDeploymente8a33c953e"
},
"RestApiId": {
"Ref": "ServerlessRestApi"
Expand Down
6 changes: 3 additions & 3 deletions tests/translator/output/aws-cn/explicit_api.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"GetHtmlApiDeploymentf117c932f7": {
"GetHtmlApiDeploymentb28d6f4807": {
"Properties": {
"Description": "RestApi deployment id: f117c932f75cfa87d23dfed64e9430d0081ef289",
"Description": "RestApi deployment id: b28d6f4807cb961a23c29eeed9b708cab7c109f0",
"RestApiId": {
"Ref": "GetHtmlApi"
},
Expand All @@ -82,7 +82,7 @@
"GetHtmlApiStage": {
"Properties": {
"DeploymentId": {
"Ref": "GetHtmlApiDeploymentf117c932f7"
"Ref": "GetHtmlApiDeploymentb28d6f4807"
},
"RestApiId": {
"Ref": "GetHtmlApi"
Expand Down
6 changes: 3 additions & 3 deletions tests/translator/output/aws-cn/explicit_api_openapi_3.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"GetHtmlApiDeploymentf117c932f7": {
"GetHtmlApiDeploymentb28d6f4807": {
"Properties": {
"Description": "RestApi deployment id: f117c932f75cfa87d23dfed64e9430d0081ef289",
"Description": "RestApi deployment id: b28d6f4807cb961a23c29eeed9b708cab7c109f0",
"RestApiId": {
"Ref": "GetHtmlApi"
},
Expand All @@ -81,7 +81,7 @@
"GetHtmlApiStage": {
"Properties": {
"DeploymentId": {
"Ref": "GetHtmlApiDeploymentf117c932f7"
"Ref": "GetHtmlApiDeploymentb28d6f4807"
},
"RestApiId": {
"Ref": "GetHtmlApi"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"GetHtmlApiDeploymentf117c932f7": {
"GetHtmlApiDeployment2acb558823": {
"Properties": {
"Description": "RestApi deployment id: f117c932f75cfa87d23dfed64e9430d0081ef289",
"Description": "RestApi deployment id: 2acb558823d4068b07590e8cd40633b8597a3157",
"RestApiId": {
"Ref": "GetHtmlApi"
},
Expand All @@ -37,7 +37,7 @@
"GetHtmlApiStage": {
"Properties": {
"DeploymentId": {
"Ref": "GetHtmlApiDeploymentf117c932f7"
"Ref": "GetHtmlApiDeployment2acb558823"
},
"RestApiId": {
"Ref": "GetHtmlApi"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ExplicitApiDeployment9a254aa466": {
"ExplicitApiDeployment31e389dd75": {
"Properties": {
"Description": "RestApi deployment id: 9a254aa466c6f818951dfb6e45fde65489beb153",
"Description": "RestApi deployment id: 31e389dd75bba46beb7a57c4256c48c5a9b127f4",
"RestApiId": {
"Ref": "ExplicitApi"
},
Expand All @@ -85,7 +85,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ExplicitApiDeployment9a254aa466"
"Ref": "ExplicitApiDeployment31e389dd75"
},
"RestApiId": {
"Ref": "ExplicitApi"
Expand Down Expand Up @@ -253,9 +253,9 @@
},
"Type": "AWS::ApiGateway::RestApi"
},
"ServerlessRestApiDeploymente1212668e0": {
"ServerlessRestApiDeployment409ed00828": {
"Properties": {
"Description": "RestApi deployment id: e1212668e096994ab32167666f5a877bd6ac5fad",
"Description": "RestApi deployment id: 409ed008280bb6489cdd1707ff7f2b66d5ad40bb",
"RestApiId": {
"Ref": "ServerlessRestApi"
},
Expand All @@ -268,7 +268,7 @@
"CacheClusterEnabled": true,
"CacheClusterSize": "1.6",
"DeploymentId": {
"Ref": "ServerlessRestApiDeploymente1212668e0"
"Ref": "ServerlessRestApiDeployment409ed00828"
},
"RestApiId": {
"Ref": "ServerlessRestApi"
Expand Down
Loading