11
11
from typing import Any , Dict
12
12
from uuid import uuid4
13
13
14
+ import jsonpatch
14
15
import yaml
15
16
from botocore .exceptions import ClientError , WaiterError
16
17
from jinja2 import Environment , PackageLoader , select_autoescape
63
64
TARGET_CANARY_FOLDER = "canary-bundle/canary"
64
65
RPDK_CONFIG_FILE = ".rpdk-config"
65
66
CANARY_FILE_PREFIX = "canary"
67
+ CANARY_FILE_CREATE_SUFFIX = "001"
68
+ CANARY_FILE_UPDATE_SUFFIX = "002"
69
+ CANARY_SUPPORTED_PATCH_INPUT_OPERATIONS = {"replace" , "remove" , "add" }
70
+ CREATE_INPUTS_KEY = "CreateInputs"
71
+ PATCH_INPUTS_KEY = "PatchInputs"
72
+ PATCH_VALUE_KEY = "value"
73
+ PATCH_OPERATION_KEY = "op"
66
74
CONTRACT_TEST_DEPENDENCY_FILE_NAME = "dependencies.yml"
67
75
CANARY_DEPENDENCY_FILE_NAME = "bootstrap.yaml"
68
76
CANARY_SETTINGS = "canarySettings"
76
84
FILE_GENERATION_ENABLED = "file_generation_enabled"
77
85
TYPE_NAME = "typeName"
78
86
CONTRACT_TEST_FILE_NAMES = "contract_test_file_names"
79
- INPUT1_FILE_NAME = "inputs_1.json"
80
87
FN_SUB = "Fn::Sub"
81
88
FN_IMPORT_VALUE = "Fn::ImportValue"
82
89
UUID = "uuid"
@@ -1345,21 +1352,68 @@ def _generate_stack_template_files(self) -> None:
1345
1352
with ct_file .open ("r" ) as f :
1346
1353
json_data = json .load (f )
1347
1354
resource_name = self .type_info [2 ]
1348
- stack_template_data = {
1349
- "Description" : f"Template for { self .type_name } " ,
1350
- "Resources" : {
1351
- f"{ resource_name } " : {
1352
- "Type" : self .type_name ,
1353
- "Properties" : self ._replace_dynamic_values (
1354
- json_data ["CreateInputs" ]
1355
- ),
1356
- }
1357
- },
1358
- }
1359
- stack_template_file_name = f"{ CANARY_FILE_PREFIX } { count } _001.yaml"
1360
- stack_template_file_path = stack_template_folder / stack_template_file_name
1361
- with stack_template_file_path .open ("w" ) as stack_template_file :
1362
- yaml .dump (stack_template_data , stack_template_file , indent = 2 )
1355
+
1356
+ self ._save_stack_template_data (
1357
+ resource_name ,
1358
+ count ,
1359
+ stack_template_folder ,
1360
+ self ._replace_dynamic_values (
1361
+ json_data [CREATE_INPUTS_KEY ],
1362
+ ),
1363
+ CANARY_FILE_CREATE_SUFFIX ,
1364
+ )
1365
+ if PATCH_INPUTS_KEY in json_data :
1366
+ supported_patch_inputs = self ._translate_supported_patch_inputs (
1367
+ json_data [PATCH_INPUTS_KEY ]
1368
+ )
1369
+ patch_data = jsonpatch .apply_patch (
1370
+ json_data [CREATE_INPUTS_KEY ], supported_patch_inputs , in_place = False
1371
+ )
1372
+ self ._save_stack_template_data (
1373
+ resource_name ,
1374
+ count ,
1375
+ stack_template_folder ,
1376
+ patch_data ,
1377
+ CANARY_FILE_UPDATE_SUFFIX ,
1378
+ )
1379
+
1380
+ def _save_stack_template_data (
1381
+ self ,
1382
+ resource_name ,
1383
+ contract_test_input_count ,
1384
+ stack_template_folder ,
1385
+ properties_data ,
1386
+ suffix ,
1387
+ ):
1388
+ stack_template_data = {
1389
+ "Description" : f"Template for { self .type_name } " ,
1390
+ "Resources" : {
1391
+ f"{ resource_name } " : {
1392
+ "Type" : self .type_name ,
1393
+ "Properties" : properties_data ,
1394
+ }
1395
+ },
1396
+ }
1397
+ stack_template_file_name = (
1398
+ f"{ CANARY_FILE_PREFIX } { contract_test_input_count } _{ suffix } .yaml"
1399
+ )
1400
+ stack_template_file_path = stack_template_folder / stack_template_file_name
1401
+ with stack_template_file_path .open ("w" ) as stack_template_file :
1402
+ yaml .dump (stack_template_data , stack_template_file , indent = 2 )
1403
+
1404
+ def _translate_supported_patch_inputs (self , patch_inputs : Any ) -> Any :
1405
+ output = []
1406
+ for patch_input in patch_inputs :
1407
+ if (
1408
+ patch_input .get (PATCH_OPERATION_KEY )
1409
+ in CANARY_SUPPORTED_PATCH_INPUT_OPERATIONS
1410
+ ):
1411
+ if PATCH_VALUE_KEY in patch_input :
1412
+ self ._replace_dynamic_values_with_root_key (
1413
+ patch_input , PATCH_VALUE_KEY
1414
+ )
1415
+ output .append (patch_input )
1416
+ return output
1363
1417
1364
1418
def _replace_dynamic_values (self , properties : Dict [str , Any ]) -> Dict [str , Any ]:
1365
1419
for key , value in properties .items ():
@@ -1372,6 +1426,19 @@ def _replace_dynamic_values(self, properties: Dict[str, Any]) -> Dict[str, Any]:
1372
1426
properties [key ] = return_value
1373
1427
return properties
1374
1428
1429
+ def _replace_dynamic_values_with_root_key (
1430
+ self , properties : Dict [str , Any ], root_key = None
1431
+ ) -> Dict [str , Any ]:
1432
+ value = properties [root_key ]
1433
+ if isinstance (value , dict ):
1434
+ properties [root_key ] = self ._replace_dynamic_values (value )
1435
+ elif isinstance (value , list ):
1436
+ properties [root_key ] = [self ._replace_dynamic_value (item ) for item in value ]
1437
+ else :
1438
+ return_value = self ._replace_dynamic_value (value )
1439
+ properties [root_key ] = return_value
1440
+ return properties
1441
+
1375
1442
def _replace_dynamic_value (self , original_value : Any ) -> Any :
1376
1443
pattern = r"\{\{(.*?)\}\}"
1377
1444
0 commit comments