1
1
import logging
2
2
from collections import namedtuple
3
+ from dataclasses import dataclass
3
4
from typing import Any , Dict , List , Optional , Set , Tuple , Union , cast
4
5
5
6
from samtranslator .metrics .method_decorator import cw_timer
7
+ from samtranslator .model import Resource
6
8
from samtranslator .model .apigateway import (
7
9
ApiGatewayApiKey ,
8
10
ApiGatewayAuthorizer ,
67
69
GatewayResponseProperties = ["ResponseParameters" , "ResponseTemplates" , "StatusCode" ]
68
70
69
71
72
+ @dataclass
73
+ class ApiDomainResponse :
74
+ domain : Optional [ApiGatewayDomainName ]
75
+ apigw_basepath_mapping_list : Optional [List [ApiGatewayBasePathMapping ]]
76
+ recordset_group : Any
77
+
78
+
70
79
class SharedApiUsagePlan :
71
80
"""
72
81
Collects API information from different API resources in the same template,
@@ -443,12 +452,12 @@ def _construct_stage(
443
452
444
453
def _construct_api_domain ( # noqa: too-many-branches
445
454
self , rest_api : ApiGatewayRestApi , route53_record_set_groups : Any
446
- ) -> Tuple [ Optional [ ApiGatewayDomainName ], Optional [ List [ ApiGatewayBasePathMapping ]], Any ] :
455
+ ) -> ApiDomainResponse :
447
456
"""
448
457
Constructs and returns the ApiGateway Domain and BasepathMapping
449
458
"""
450
459
if self .domain is None :
451
- return None , None , None
460
+ return ApiDomainResponse ( None , None , None )
452
461
453
462
sam_expect (self .domain , self .logical_id , "Domain" ).to_be_a_map ()
454
463
domain_name : PassThrough = sam_expect (
@@ -565,6 +574,17 @@ def _construct_api_domain( # noqa: too-many-branches
565
574
logical_id = "RecordSetGroup" + logical_id_suffix
566
575
567
576
record_set_group = route53_record_set_groups .get (logical_id )
577
+
578
+ if route53 .get ("SeparateRecordSetGroup" ):
579
+ sam_expect (
580
+ route53 .get ("SeparateRecordSetGroup" ), self .logical_id , "Domain.Route53.SeparateRecordSetGroup"
581
+ ).to_be_a_bool ()
582
+ return ApiDomainResponse (
583
+ domain ,
584
+ basepath_resource_list ,
585
+ self ._construct_single_record_set_group (self .domain , api_domain_name , route53 ),
586
+ )
587
+
568
588
if not record_set_group :
569
589
record_set_group = Route53RecordSetGroup (logical_id , attributes = self .passthrough_resource_attributes )
570
590
if "HostedZoneId" in route53 :
@@ -576,27 +596,46 @@ def _construct_api_domain( # noqa: too-many-branches
576
596
577
597
record_set_group .RecordSets += self ._construct_record_sets_for_domain (self .domain , api_domain_name , route53 )
578
598
579
- return domain , basepath_resource_list , record_set_group
599
+ return ApiDomainResponse (domain , basepath_resource_list , record_set_group )
600
+
601
+ def _construct_single_record_set_group (
602
+ self , domain : Dict [str , Any ], api_domain_name : str , route53 : Any
603
+ ) -> Route53RecordSetGroup :
604
+ hostedZoneId = route53 .get ("HostedZoneId" )
605
+ hostedZoneName = route53 .get ("HostedZoneName" )
606
+ domainName = domain .get ("DomainName" )
607
+ logical_id = logical_id = LogicalIdGenerator (
608
+ "RecordSetGroup" , [hostedZoneId or hostedZoneName , domainName ]
609
+ ).gen ()
610
+
611
+ record_set_group = Route53RecordSetGroup (logical_id , attributes = self .passthrough_resource_attributes )
612
+ if hostedZoneId :
613
+ record_set_group .HostedZoneId = hostedZoneId
614
+ if hostedZoneName :
615
+ record_set_group .HostedZoneName = hostedZoneName
616
+
617
+ record_set_group .RecordSets = []
618
+ record_set_group .RecordSets += self ._construct_record_sets_for_domain (domain , api_domain_name , route53 )
619
+
620
+ return record_set_group
580
621
581
622
def _construct_record_sets_for_domain (
582
623
self , custom_domain_config : Dict [str , Any ], api_domain_name : str , route53_config : Dict [str , Any ]
583
624
) -> List [Dict [str , Any ]]:
584
625
recordset_list = []
585
-
626
+ alias_target = self . _construct_alias_target ( custom_domain_config , api_domain_name , route53_config )
586
627
recordset = {}
587
628
recordset ["Name" ] = custom_domain_config .get ("DomainName" )
588
629
recordset ["Type" ] = "A"
589
- recordset ["AliasTarget" ] = self . _construct_alias_target ( custom_domain_config , api_domain_name , route53_config )
630
+ recordset ["AliasTarget" ] = alias_target
590
631
self ._update_route53_routing_policy_properties (route53_config , recordset )
591
632
recordset_list .append (recordset )
592
633
593
634
if route53_config .get ("IpV6" ) is not None and route53_config .get ("IpV6" ) is True :
594
635
recordset_ipv6 = {}
595
636
recordset_ipv6 ["Name" ] = custom_domain_config .get ("DomainName" )
596
637
recordset_ipv6 ["Type" ] = "AAAA"
597
- recordset_ipv6 ["AliasTarget" ] = self ._construct_alias_target (
598
- custom_domain_config , api_domain_name , route53_config
599
- )
638
+ recordset_ipv6 ["AliasTarget" ] = alias_target
600
639
self ._update_route53_routing_policy_properties (route53_config , recordset_ipv6 )
601
640
recordset_list .append (recordset_ipv6 )
602
641
@@ -626,14 +665,20 @@ def _construct_alias_target(self, domain: Dict[str, Any], api_domain_name: str,
626
665
return alias_target
627
666
628
667
@cw_timer (prefix = "Generator" , name = "Api" )
629
- def to_cloudformation (self , redeploy_restapi_parameters , route53_record_set_groups ): # type: ignore[no-untyped-def]
668
+ def to_cloudformation (
669
+ self , redeploy_restapi_parameters : Optional [Any ], route53_record_set_groups : Dict [str , Route53RecordSetGroup ]
670
+ ) -> List [Resource ]:
630
671
"""Generates CloudFormation resources from a SAM API resource
631
672
632
673
:returns: a tuple containing the RestApi, Deployment, and Stage for an empty Api.
633
674
:rtype: tuple
634
675
"""
635
676
rest_api = self ._construct_rest_api ()
636
- domain , basepath_mapping , route53 = self ._construct_api_domain (rest_api , route53_record_set_groups )
677
+ api_domain_response = self ._construct_api_domain (rest_api , route53_record_set_groups )
678
+ domain = api_domain_response .domain
679
+ basepath_mapping = api_domain_response .apigw_basepath_mapping_list
680
+ route53_recordsetGroup = api_domain_response .recordset_group
681
+
637
682
deployment = self ._construct_deployment (rest_api )
638
683
639
684
swagger = None
@@ -646,7 +691,41 @@ def to_cloudformation(self, redeploy_restapi_parameters, route53_record_set_grou
646
691
permissions = self ._construct_authorizer_lambda_permission ()
647
692
usage_plan = self ._construct_usage_plan (rest_api_stage = stage )
648
693
649
- return rest_api , deployment , stage , permissions , domain , basepath_mapping , route53 , usage_plan
694
+ # mypy complains if the type in List doesn't match exactly
695
+ # TODO: refactor to have a list of single resource
696
+ generated_resources : List [
697
+ Union [
698
+ Optional [Resource ],
699
+ List [Resource ],
700
+ Tuple [Resource ],
701
+ List [LambdaPermission ],
702
+ List [ApiGatewayBasePathMapping ],
703
+ ],
704
+ ] = []
705
+
706
+ generated_resources .extend (
707
+ [
708
+ rest_api ,
709
+ deployment ,
710
+ stage ,
711
+ permissions ,
712
+ domain ,
713
+ basepath_mapping ,
714
+ route53_recordsetGroup ,
715
+ usage_plan ,
716
+ ]
717
+ )
718
+
719
+ # Make a list of single resources
720
+ generated_resources_list : List [Resource ] = []
721
+ for resource in generated_resources :
722
+ if resource :
723
+ if isinstance (resource , (list , tuple )):
724
+ generated_resources_list .extend (resource )
725
+ else :
726
+ generated_resources_list .extend ([resource ])
727
+
728
+ return generated_resources_list
650
729
651
730
def _add_cors (self ) -> None :
652
731
"""
0 commit comments