diff --git a/CHANGELOG.next.md b/CHANGELOG.next.md index 113b16e8e6..bac4b790cb 100644 --- a/CHANGELOG.next.md +++ b/CHANGELOG.next.md @@ -45,6 +45,7 @@ Thanks, you're awesome :-) --> #### Added * Support `match_only_text` data type in Go code generator. #1418 +* Support for multi-level, self-nestings. #1459 #### Improvements diff --git a/scripts/schema/finalizer.py b/scripts/schema/finalizer.py index 648349b8ef..86fe5b760f 100644 --- a/scripts/schema/finalizer.py +++ b/scripts/schema/finalizer.py @@ -89,7 +89,11 @@ def perform_reuse(fields): new_field_details['name'] = nest_as new_field_details['original_fieldset'] = schema_name new_field_details['intermediate'] = True - destination_fields = schema['fields'] + # to handle multi-level self-nesting + if reuse_entry['at'] != schema_name: + destination_fields = field_group_at_path(reuse_entry['at'], fields) + else: + destination_fields = schema['fields'] destination_fields[nest_as] = { 'field_details': new_field_details, # Make a new copy of the pristine copy diff --git a/scripts/tests/unit/test_schema_finalizer.py b/scripts/tests/unit/test_schema_finalizer.py index 96c909cc09..fd0c8d6118 100644 --- a/scripts/tests/unit/test_schema_finalizer.py +++ b/scripts/tests/unit/test_schema_finalizer.py @@ -50,6 +50,8 @@ def schema_process(self): 'short_override': 'short override desc'}, {'full': 'reuse.process', 'at': 'reuse', 'as': 'process'}, {'full': 'reuse.process.parent', 'at': 'reuse.process', 'as': 'parent'}, + {'full': 'reuse.process.target', 'at': 'reuse.process', 'as': 'target'}, + {'full': 'reuse.process.target.parent', 'at': 'reuse.process.target', 'as': 'parent'} ] } }, @@ -179,24 +181,28 @@ def test_perform_reuse_with_foreign_reuse_and_self_reuse(self): server_fields = fields['server']['fields'] user_fields = fields['user']['fields'] process_reuse_fields = fields['reuse']['fields']['process']['fields'] + process_target_reuse_fields = fields['reuse']['fields']['process']['fields']['target']['fields'] # Expected reuse self.assertIn('parent', process_fields) self.assertIn('user', server_fields) self.assertIn('target', user_fields) self.assertIn('effective', user_fields) self.assertIn('parent', process_reuse_fields) + self.assertIn('parent', process_target_reuse_fields) # Sanity check for presence of leaf fields, after performing reuse self.assertIn('name', user_fields['target']['fields']) self.assertIn('name', user_fields['effective']['fields']) self.assertIn('name', server_fields['user']['fields']) self.assertIn('pid', process_fields['parent']['fields']) self.assertIn('pid', process_reuse_fields['parent']['fields']) + self.assertIn('pid', process_target_reuse_fields['parent']['fields']) # Ensure the parent field of reused fields is marked as intermediate self.assertTrue(server_fields['user']['field_details']['intermediate']) self.assertTrue(process_fields['parent']['field_details']['intermediate']) self.assertTrue(user_fields['target']['field_details']['intermediate']) self.assertTrue(user_fields['effective']['field_details']['intermediate']) self.assertTrue(process_reuse_fields['parent']['field_details']['intermediate']) + self.assertTrue(process_target_reuse_fields['parent']['field_details']['intermediate']) # No unexpected cross-nesting self.assertNotIn('target', user_fields['target']['fields']) self.assertNotIn('target', user_fields['effective']['fields']) @@ -207,6 +213,7 @@ def test_perform_reuse_with_foreign_reuse_and_self_reuse(self): self.assertIn('user.target', fields['user']['schema_details']['nestings']) self.assertIn('server.user', fields['server']['schema_details']['nestings']) self.assertIn('reuse.process.parent', fields['reuse']['schema_details']['nestings']) + self.assertIn('reuse.process.target.parent', fields['reuse']['schema_details']['nestings']) # Attribute 'reused_here' lists nestings inside a destination schema self.assertIn({'full': 'process.parent', 'schema_name': 'process', 'short': 'short override desc'}, fields['process']['schema_details']['reused_here']) @@ -218,6 +225,8 @@ def test_perform_reuse_with_foreign_reuse_and_self_reuse(self): fields['server']['schema_details']['reused_here']) self.assertIn({'full': 'reuse.process.parent', 'schema_name': 'process', 'short': 'short desc'}, fields['reuse']['schema_details']['reused_here']) + self.assertIn({'full': 'reuse.process.target.parent', 'schema_name': 'process', 'short': 'short desc'}, + fields['reuse']['schema_details']['reused_here']) # Reused fields have an indication they're reused self.assertEqual(process_fields['parent']['field_details']['original_fieldset'], 'process', "The parent field of reused fields should have 'original_fieldset' populated") @@ -228,6 +237,8 @@ def test_perform_reuse_with_foreign_reuse_and_self_reuse(self): self.assertEqual(server_fields['user']['fields']['name']['field_details']['original_fieldset'], 'user') self.assertEqual(process_reuse_fields['parent']['field_details']['original_fieldset'], 'process', "The parent field of reused fields should have 'original_fieldset' populated") + self.assertEqual(process_target_reuse_fields['parent']['field_details']['original_fieldset'], 'process', + "The parent field of reused fields should have 'original_fieldset' populated") # Original fieldset's fields must not be marked with 'original_fieldset=' self.assertNotIn('original_fieldset', user_fields['name']['field_details']) self.assertNotIn('original_fieldset', process_fields['pid']['field_details'])