async def test_conditional_proj_using_object_model(self):
        """Test for creating a projection with an AddCountAttribute operation and a condition using the object model"""
        corpus = ProjectionTestUtils.get_local_corpus(self.tests_subpath, 'test_conditional_proj_using_object_model')
        corpus.storage.mount('local', LocalAdapter(TestHelper.get_actual_output_folder_path(self.tests_subpath, 'test_conditional_proj_using_object_model')))
        local_root = corpus.storage.fetch_root_folder('local')

        # Create an entity
        entity = ProjectionTestUtils.create_entity(corpus, local_root)

        # Create a projection with a condition that states the operation should only execute when the resolution directive is 'referenceOnly'
        projection = ProjectionTestUtils.create_projection(corpus, local_root)
        projection.condition = 'referenceOnly==True'

        # Create an AddCountAttribute operation
        add_count_attr_op = corpus.make_object(CdmObjectType.OPERATION_ADD_COUNT_ATTRIBUTE_DEF)
        add_count_attr_op.count_attribute = corpus.make_object(CdmObjectType.TYPE_ATTRIBUTE_DEF, 'testCount')
        add_count_attr_op.count_attribute.data_type = corpus.make_ref(CdmObjectType.DATA_TYPE_REF, 'integer', True)
        projection.operations.append(add_count_attr_op)

        # Create an entity reference to hold this projection
        projection_entity_ref = corpus.make_object(CdmObjectType.ENTITY_REF, None)
        projection_entity_ref.explicit_reference = projection

        # Create an entity attribute that contains this projection and add this to the entity
        entity_attribute = corpus.make_object(CdmObjectType.ENTITY_ATTRIBUTE_DEF, 'TestEntityAttribute')
        entity_attribute.entity = projection_entity_ref
        entity.attributes.append(entity_attribute)

        # Create resolution options with the 'referenceOnly' directive
        res_opt = ResolveOptions(entity.in_document)
        res_opt.directives = AttributeResolutionDirectiveSet(set(['referenceOnly']))

        # Resolve the entity with 'referenceOnly'
        resolved_entity_with_reference_only = await entity.create_resolved_entity_async('Resolved_{}.cdm.json'.format(entity.entity_name), res_opt, local_root)

        # Verify correctness of the resolved attributes after running the AddCountAttribute operation
        # Original set of attributes: ["id", "name", "value", "date"]
        # Count attribute: "testCount"
        self.assertEqual(5, len(resolved_entity_with_reference_only.attributes))
        self.assertEqual('id', resolved_entity_with_reference_only.attributes[0].name)
        self.assertEqual('name', resolved_entity_with_reference_only.attributes[1].name)
        self.assertEqual('value', resolved_entity_with_reference_only.attributes[2].name)
        self.assertEqual('date', resolved_entity_with_reference_only.attributes[3].name)
        self.assertEqual('testCount', resolved_entity_with_reference_only.attributes[4].name)
        self.assertIsNotNone(resolved_entity_with_reference_only.attributes[4].applied_traits.item('is.linkedEntity.array.count'))

        # Now resolve the entity with the 'structured' directive
        res_opt.directives = AttributeResolutionDirectiveSet(set(['structured']))
        resolved_entity_with_structured = await entity.create_resolved_entity_async('Resolved_{}.cdm.json'.format(entity.entity_name), res_opt, local_root)

        # Verify correctness of the resolved attributes after running the AddCountAttribute operation
        # Original set of attributes: ["id", "name", "value", "date"]
        # No Count attribute added, condition was false
        self.assertEqual(4, len(resolved_entity_with_structured.attributes))
        self.assertEqual('id', resolved_entity_with_structured.attributes[0].name)
        self.assertEqual('name', resolved_entity_with_structured.attributes[1].name)
        self.assertEqual('value', resolved_entity_with_structured.attributes[2].name)
        self.assertEqual('date', resolved_entity_with_structured.attributes[3].name)
    async def test_conditional_proj_using_object_model(self):
        """Test for creating a projection with an AddAttributeGroup operation and a condition using the object model"""
        test_name = 'test_conditional_proj_using_object_model'
        corpus = ProjectionTestUtils.get_corpus(test_name, self.tests_subpath)
        local_root = corpus.storage.fetch_root_folder('local')

        # Create an entity.
        entity = ProjectionTestUtils.create_entity(corpus, local_root)

        # Create a projection with a condition that states the operation should only execute when the resolution directive is 'structured'.
        projection = ProjectionTestUtils.create_projection(corpus, local_root)
        projection.condition = 'structured==true'

        # Create an AddAttributeGroup operation
        add_att_group_op = corpus.make_object(CdmObjectType.OPERATION_ADD_ATTRIBUTE_GROUP_DEF)
        add_att_group_op.attribute_group_name = 'PersonAttributeGroup'
        projection.operations.append(add_att_group_op)

        # Create an entity reference to hold this projection.
        projection_entity_ref = corpus.make_object(CdmObjectType.ENTITY_REF, None)  # type: CdmEntityReference
        projection_entity_ref.explicit_reference = projection

        # Create an entity attribute that contains this projection and add this to the entity.
        entity_attribute = corpus.make_object(CdmObjectType.ENTITY_ATTRIBUTE_DEF, 'TestEntityAttribute')  # type: CdmEntityAttributeDefinition
        entity_attribute.entity = projection_entity_ref
        entity.attributes.append(entity_attribute)

        # Create resolution options with the 'referenceOnly' directive.
        res_opt = ResolveOptions(entity.in_document)
        res_opt.directives = AttributeResolutionDirectiveSet({'referenceOnly'})

        # Resolve the entity with 'referenceOnly'
        resolved_entity_with_reference_only = await entity.create_resolved_entity_async('Resolved_{}.cdm.json'.format(entity.entity_name), res_opt, local_root)

        # Verify correctness of the resolved attributes after running the AddAttributeGroup operation
        # Original set of attributes: ['id', 'name', 'value', 'date']
        # Condition not met, keep attributes in flat list
        self.assertEqual(4, len(resolved_entity_with_reference_only.attributes))
        self.assertEqual('id', resolved_entity_with_reference_only.attributes[0].name)
        self.assertEqual('name', resolved_entity_with_reference_only.attributes[1].name)
        self.assertEqual('value', resolved_entity_with_reference_only.attributes[2].name)
        self.assertEqual('date', resolved_entity_with_reference_only.attributes[3].name)

        # Now resolve the entity with the 'structured' directive
        res_opt.directives = AttributeResolutionDirectiveSet({'structured'})
        resolved_entity_with_structured = await entity.create_resolved_entity_async('Resolved_{}.cdm.json'.format(entity.entity_name), res_opt, local_root)

        # Verify correctness of the resolved attributes after running the AddAttributeGroup operation
        # Original set of attributes: ['id', 'name', 'value', 'date']
        # Condition met, put all attributes in an attribute group
        att_group_definition = self.validate_attribute_group(resolved_entity_with_structured.attributes, 'PersonAttributeGroup')
        self.assertEqual(4, len(att_group_definition.members))
        self.assertEqual('id', att_group_definition.members[0].name)
        self.assertEqual('name', att_group_definition.members[1].name)
        self.assertEqual('value', att_group_definition.members[2].name)
        self.assertEqual('date', att_group_definition.members[3].name)
Example #3
0
    async def test_setting_traits_for_resolution_guidance_attributes(self):
        '''
        Test that "is.linkedEntity.name" and "is.linkedEntity.identifier" traits are set when "selectedTypeAttribute" and "foreignKeyAttribute"
        are present in the entity's resolution guidance.
        '''
        corpus = TestHelper.get_local_corpus(
            self.tests_subpath,
            'test_setting_traits_for_resolution_guidance_attributes'
        )  # type: CdmCorpusDefinition
        entity = await corpus.fetch_object_async(
            'local:/Customer.cdm.json/Customer')  # type: CdmEntityDefinition

        # Resolve with default directives to get "is.linkedEntity.name" trait.
        res_opt = ResolveOptions(wrt_doc=entity.in_document)
        resolved_entity = await entity.create_resolved_entity_async(
            'resolved', res_opt)

        self.assertEqual(
            'is.linkedEntity.name',
            resolved_entity.attributes[1].applied_traits[6].named_reference)

        # Resolve with referenceOnly directives to get "is.linkedEntity.identifier" trait.
        res_opt = ResolveOptions(wrt_doc=entity.in_document)
        res_opt.directives = AttributeResolutionDirectiveSet({'referenceOnly'})
        resolved_entity = await entity.create_resolved_entity_async(
            'resolved2', res_opt)

        self.assertEqual(
            'is.linkedEntity.identifier',
            resolved_entity.attributes[0].applied_traits[7].named_reference)
Example #4
0
        async def seek_entities(f: 'CdmManifestDefinition'):
            if f.entities is not None:
                spew.spew_line(f.folder_path)

                for entity in f.entities:
                    ent = entity
                    current_file = f
                    while isinstance(ent, CdmReferencedEntityDeclarationDefinition):
                        corpus_path = corpus.storage.create_absolute_corpus_path(ent.entity_path, current_file)
                        ent = await corpus.fetch_object_async(corpus_path)
                        current_file = ent

                    corpus_path = corpus.storage.create_absolute_corpus_path(ent.entity_path, current_file)
                    res_opt = ResolveOptions()
                    res_opt.imports_load_strategy = ImportsLoadStrategy.LOAD
                    new_ent = await corpus.fetch_object_async(corpus_path, res_opt=res_opt)
                    res_opt.wrt_doc = new_ent.in_document
                    res_opt.directives = directives
                    res_ent = ResolvedEntity(res_opt, new_ent)

                    res_ent.spew(res_opt, spew, ' ', True)

            if f.sub_manifests:
                for sub_manifest in f.sub_manifests:
                    corpus_path = corpus.storage.create_absolute_corpus_path(sub_manifest.definition, f)
                    await seek_entities(await corpus.fetch_object_async(corpus_path))
Example #5
0
    def _copy_resolve_options(res_opt: 'ResolveOptions') -> 'ResolveOptions':
        from cdm.utilities import ResolveOptions  # pylint: disable=redefined-outer-name
        res_opt_copy = ResolveOptions()
        res_opt_copy.wrt_doc = res_opt.wrt_doc
        res_opt_copy._relationship_depth = res_opt._relationship_depth
        res_opt_copy._localize_references_for = res_opt._localize_references_for
        res_opt_copy._indexing_doc = res_opt._indexing_doc

        if res_opt.directives:
            res_opt_copy.directives = res_opt.directives.copy()

        return res_opt_copy
Example #6
0
    async def test_resolved_attribute_limit(self):
        expected_log_codes = { CdmLogCode.ERR_REL_MAX_RESOLVED_ATTR_REACHED }
        corpus = TestHelper.get_local_corpus(self.tests_sub_path, 'test_resolved_attribute_limit', expected_codes=expected_log_codes)  # type: CdmCorpusDefinition

        main_entity = await corpus.fetch_object_async('local:/mainEntity.cdm.json/mainEntity')  # type: CdmEntityDefinition
        res_opt = ResolveOptions(wrt_doc=main_entity.in_document)

        # if attribute limit is reached, entity should be None
        res_opt._resolved_attribute_limit = 4
        resEnt = await main_entity.create_resolved_entity_async('{}_zeroAtts'.format(main_entity.entity_name), res_opt)  # type: CdmEntityDefinition
        self.assertIsNone(resEnt)

        # when the attribute limit is set to null, there should not be a limit on the possible number of attributes
        res_opt._resolved_attribute_limit = None
        ras = main_entity._fetch_resolved_attributes(res_opt)  # type: ResolvedAttributeSet
        resEnt = await main_entity.create_resolved_entity_async('{}_normalized_referenceOnly'.format(main_entity.entity_name), res_opt)

        # there are 5 total attributes
        self.assertEqual(5, ras._resolved_attribute_count)
        self.assertEqual(5, len(ras._set))
        self.assertEqual(3, len(main_entity.attributes))
        # there are 2 attributes grouped in an entity attribute
        # and 2 attributes grouped in an attribute group
        self.assertEqual(2, len(main_entity.attributes[2].explicit_reference.members))

        # using the default limit number
        res_opt = ResolveOptions(wrt_doc=main_entity.in_document)
        ras = main_entity._fetch_resolved_attributes(res_opt)
        resEnt = await main_entity.create_resolved_entity_async('{}_normalized_referenceOnly'.format(main_entity.entity_name), res_opt)

        # there are 5 total attributes
        self.assertEqual(5, ras._resolved_attribute_count)
        self.assertEqual(5, len(ras._set))
        self.assertEqual(3, len(main_entity.attributes))
        # there are 2 attributes grouped in an entity attribute
        # and 2 attributes grouped in an attribute group
        self.assertEqual(2, len(main_entity.attributes[2].explicit_reference.members))

        res_opt.directives = AttributeResolutionDirectiveSet({'normalized', 'structured'})
        ras = main_entity._fetch_resolved_attributes(res_opt)
        resEnt = await main_entity.create_resolved_entity_async('{}_normalized_structured'.format(main_entity.entity_name), res_opt)

        # there are 5 total attributes
        self.assertEqual(5, ras._resolved_attribute_count)
        # the attribute count is different because one attribute is a group that contains two different attributes
        self.assertEqual(4, len(ras._set))
        self.assertEqual(3, len(main_entity.attributes))
        # again there are 2 attributes grouped in an entity attribute
        # and 2 attributes grouped in an attribute group
        self.assertEqual(2, len(main_entity.attributes[2].explicit_reference.members))
    async def test_missing_condition_in_json(self):
        """
        Test case scenario for Bug #25 from the projections internal bug bash
        Reference Link: https:#commondatamodel.visualstudio.com/CDM/_workitems/edit/25
        """
        test_name = 'test_missing_condition_in_json'

        corpus = TestHelper.get_local_corpus(self.tests_subpath, test_name)

        def callback(level, message):
            self.fail(message)
        corpus.set_event_callback(callback, CdmStatusLevel.WARNING)

        manifest = await corpus.fetch_object_async('default.manifest.cdm.json')

        entity_name = 'SalesNestedFK'
        entity = await corpus.fetch_object_async('local:/{}.cdm.json/{}'.format(entity_name, entity_name), manifest)
        self.assertIsNotNone(entity)

        res_opt = ResolveOptions(entity.in_document)
        # where, res_opts_combinations[1] == 'referenceOnly'
        res_opt.directives = AttributeResolutionDirectiveSet(self.res_opts_combinations[1])

        resolved_folder = corpus.storage.fetch_root_folder('output')

        was_info_message_received = {}
        was_info_message_received['infoMessagedReceived'] = False

        def callback_2(level, message):
            if 'CdmProjection | Optional expression missing. Implicit expression will automatically apply. | _construct_projection_context' in message:
                was_info_message_received['infoMessagedReceived'] = True
        corpus.set_event_callback(callback_2, CdmStatusLevel.INFO)

        resolved_entity = await entity.create_resolved_entity_async('Resolved_{}.cdm.json'.format(entity_name), res_opt, resolved_folder)
        self.assertIsNotNone(resolved_entity)

        self.assertTrue(was_info_message_received['infoMessagedReceived'])
    async def test_conditional_proj_using_object_model(self):
        """Test for creating a projection with an ExcludeAttributes operation and a condition using the object model"""
        corpus = TestHelper.get_local_corpus(
            self.tests_subpath, 'test_conditional_proj_using_object_model')
        corpus.storage.mount(
            'local',
            LocalAdapter(
                TestHelper.get_actual_output_folder_path(
                    self.tests_subpath,
                    'test_conditional_proj_using_object_model')))
        local_root = corpus.storage.fetch_root_folder('local')

        # Create an entity
        entity = ProjectionTestUtils.create_entity(corpus, local_root)

        # Create a projection with a condition that states the operation should only execute when the resolution directive is 'referenceOnly'
        projection = ProjectionTestUtils.create_projection(corpus, local_root)
        projection.condition = 'referenceOnly==True'

        # Create an ExcludeAttributes operation
        exclude_attrs_op = corpus.make_object(
            CdmObjectType.OPERATION_EXCLUDE_ATTRIBUTES_DEF)
        exclude_attrs_op.exclude_attributes.append('id')
        exclude_attrs_op.exclude_attributes.append('date')
        projection.operations.append(exclude_attrs_op)

        # Create an entity reference to hold this projection
        projection_entity_ref = corpus.make_object(CdmObjectType.ENTITY_REF,
                                                   None)
        projection_entity_ref.explicit_reference = projection

        # Create an entity attribute that contains this projection and add this to the entity
        entity_attribute = corpus.make_object(
            CdmObjectType.ENTITY_ATTRIBUTE_DEF, 'TestEntityAttribute')
        entity_attribute.entity = projection_entity_ref
        entity.attributes.append(entity_attribute)

        # Create resolution options with the 'referenceOnly' directive
        res_opt = ResolveOptions(entity.in_document)
        res_opt.directives = AttributeResolutionDirectiveSet(
            set(['referenceOnly']))

        # Resolve the entity with 'referenceOnly'
        resolved_entity_with_reference_only = await entity.create_resolved_entity_async(
            'Resolved_{}.cdm.json'.format(entity.entity_name), res_opt,
            local_root)

        # Verify correctness of the resolved attributes after running the ExcludeAttributes operation
        # Original set of attributes: ['id', 'name', 'value', 'date']
        # Excluded attributes: ['id', 'date']
        self.assertEqual(2,
                         len(resolved_entity_with_reference_only.attributes))
        self.assertEqual(
            'name', resolved_entity_with_reference_only.attributes[0].name)
        self.assertEqual(
            'value', resolved_entity_with_reference_only.attributes[1].name)

        # Now resolve the entity with the 'structured' directive
        res_opt.directives = AttributeResolutionDirectiveSet(
            set(['structured']))
        resolved_entity_with_structured = await entity.create_resolved_entity_async(
            'Resolved_{}.cdm.json'.format(entity.entity_name), res_opt,
            local_root)

        # Verify correctness of the resolved attributes after running the ExcludeAttributes operation
        # Original set of attributes: ['id', 'name', 'value', 'date']
        # Excluded attributes: none, condition was false
        self.assertEqual(4, len(resolved_entity_with_structured.attributes))
        self.assertEqual('id',
                         resolved_entity_with_structured.attributes[0].name)
        self.assertEqual('name',
                         resolved_entity_with_structured.attributes[1].name)
        self.assertEqual('value',
                         resolved_entity_with_structured.attributes[2].name)
        self.assertEqual('date',
                         resolved_entity_with_structured.attributes[3].name)
Example #9
0
async def logical_manipulation_using_projections():
    '''This sample demonstrates how to model a set of common scenarios using projections. 
    The projections feature provides a way to customize the definition of a logical entity by influencing how the entity is resolved by the object model.
    Here we will model three common use cases for using projections that are associated with the directives 'referenceOnly', 'structured' and 'normalized'.
    A single logical definition can be resolved into multiple physical layouts. The directives are used to instruct the ObjectModel about how it should to
    resolve the logical definition provided. To achieve this, we define projections that run conditionally, depending on the directives provided when
    calling create_resolved_entity_async.
    To get an overview of the projections feature as well as all of the supported operations refer to the link below.
    https://docs.microsoft.com/en-us/common-data-model/sdk/convert-logical-entities-resolved-entities#projection-overview
    '''

    # Make a corpus, the corpus is the collection of all documents and folders created or discovered while navigating objects and paths
    corpus = CdmCorpusDefinition()

    print('Configure storage adapters')

    # Configure storage adapters to point at the target local manifest location and at the fake public standards
    path_from_exe_to_example_root = '../../'

    corpus.storage.mount(
        'local',
        LocalAdapter(path_from_exe_to_example_root +
                     '8-logical-manipulation-using-projections/sample-data'))
    corpus.storage.default_namespace = 'local'  # local is our default. so any paths that start out navigating without a device tag will assume local

    # Fake cdm, normaly use the CDM Standards adapter
    # Mount it as the 'cdm' device, not the default so must use 'cdm:/folder' to get there
    corpus.storage.mount(
        'cdm',
        LocalAdapter(path_from_exe_to_example_root +
                     'example-public-standards'))

    print('Create logical entity definition.')

    logical_folder = await corpus.fetch_object_async(
        'local:/')  # type: CdmFolderDefinition

    logical_doc = logical_folder.documents.append('Person.cdm.json')
    logical_doc.imports.append('Address.cdm.json')

    entity = logical_doc.definitions.append(
        'Person')  # type: CdmEntityDefinition

    # Add 'name' data typed attribute.
    name_attr = entity.attributes.append(
        'name')  # type: CdmTypeAttributeDefinition
    name_attr.data_type = CdmDataTypeReference(corpus.ctx, 'string', True)

    # Add 'age' data typed attribute.
    age_attr = entity.attributes.append(
        'age')  # type: CdmTypeAttributeDefinition
    age_attr.data_type = CdmDataTypeReference(corpus.ctx, 'string', True)

    # Add 'address' entity typed attribute.
    entity_attr = CdmEntityAttributeDefinition(corpus.ctx, 'address')
    entity_attr.entity = CdmEntityReference(corpus.ctx, 'Address', True)
    apply_array_expansion(entity_attr, 1, 3, '{m}{A}{o}', 'countAttribute')
    apply_default_behavior(entity_attr, 'addressFK', 'address')

    entity.attributes.append(entity_attr)

    # Add 'email' data typed attribute.
    email_attr = entity.attributes.append(
        'email')  # type: CdmTypeAttributeDefinition
    email_attr.data_type = CdmDataTypeReference(corpus.ctx, 'string', True)

    # Save the logical definition of Person.
    await entity.in_document.save_as_async('Person.cdm.json')

    print('Get \'resolved\' folder where the resolved entities will be saved.')

    resolved_folder = await corpus.fetch_object_async(
        'local:/resolved/')  # type: CdmFolderDefinition

    res_opt = ResolveOptions(entity)

    # To get more information about directives and their meaning refer to
    # https://docs.microsoft.com/en-us/common-data-model/sdk/convert-logical-entities-resolved-entities#directives-guidance-and-the-resulting-resolved-shapes

    # We will start by resolving this entity with the 'normalized' direcitve.
    # This directive will be used on this and the next two examples so we can analize the resolved entity
    # without the array expansion.
    print('Resolving logical entity with normalized directive.')
    res_opt.directives = AttributeResolutionDirectiveSet({'normalized'})
    res_normalized_entity = await entity.create_resolved_entity_async(
        f'normalized_{entity.entity_name}', res_opt, resolved_folder)
    await res_normalized_entity.in_document.save_as_async(
        f'{res_normalized_entity.entity_name}.cdm.json')

    # Another common scenario is to resolve an entity using the 'referenceOnly' directive.
    # This directives is used to replace the relationships with a foreign key.
    print('Resolving logical entity with referenceOnly directive.')
    res_opt.directives = AttributeResolutionDirectiveSet(
        {'normalized', 'referenceOnly'})
    res_reference_only_entity = await entity.create_resolved_entity_async(
        f'referenceOnly_{entity.entity_name}', res_opt, resolved_folder)
    await res_reference_only_entity.in_document.save_as_async(
        f'{res_reference_only_entity.entity_name}.cdm.json')

    # When dealing with structured data, like Json or parquet, it sometimes necessary to represent the idea that
    # a property can hold a complex object. The shape of the complex object is defined by the source entity pointed by the
    # entity attribute and we use the 'structured' directive to resolve the entity attribute as an attribute group.
    print('Resolving logical entity with structured directive.')
    res_opt.directives = AttributeResolutionDirectiveSet(
        {'normalized', 'structured'})
    res_structured_entity = await entity.create_resolved_entity_async(
        f'structured_{entity.entity_name}', res_opt, resolved_folder)
    await res_structured_entity.in_document.save_as_async(
        f'{res_structured_entity.entity_name}.cdm.json')

    # Now let us remove the 'normalized' directive so the array expansion operation can run.
    print('Resolving logical entity without directives (array expansion).')
    res_opt.directives = AttributeResolutionDirectiveSet({})
    res_array_entity = await entity.create_resolved_entity_async(
        f'array_expansion_{entity.entity_name}', res_opt, resolved_folder)
    await res_array_entity.in_document.save_as_async(
        f'{res_array_entity.entity_name}.cdm.json')
Example #10
0
    async def test_conditional_proj_using_object_model(self):
        """Test for creating a projection with an AddArtifactAttribute operation and a condition using the object model"""
        test_name = 'test_conditional_proj_using_object_model'
        corpus = ProjectionTestUtils.get_local_corpus(self.tests_subpath,
                                                      test_name)
        local_root = corpus.storage.fetch_root_folder('local')

        # Create an entity.
        entity = ProjectionTestUtils.create_entity(corpus, local_root)

        # Create a projection with a condition that states the operation should only execute when the resolution directive is 'structured'.
        projection = ProjectionTestUtils.create_projection(corpus, local_root)
        projection.condition = 'structured==true'

        # Create an AddArtifactAttribute operation
        add_artifact_attribute_op = corpus.make_object(
            CdmObjectType.OPERATION_ADD_ARTIFACT_ATTRIBUTE_DEF)
        add_artifact_attribute_op.new_attribute = corpus.make_object(
            CdmObjectType.TYPE_ATTRIBUTE_DEF, "newName")
        add_artifact_attribute_op.new_attribute.data_type = corpus.make_ref(
            CdmObjectType.DATA_TYPE_REF, "string", True)
        projection.operations.append(add_artifact_attribute_op)

        # Create an entity reference to hold this projection.
        projection_entity_ref = corpus.make_object(
            CdmObjectType.ENTITY_REF, None)  # type: CdmEntityReference
        projection_entity_ref.explicit_reference = projection

        # Create an entity attribute that contains this projection and add this to the entity.
        entity_attribute = corpus.make_object(
            CdmObjectType.ENTITY_ATTRIBUTE_DEF,
            'TestEntityAttribute')  # type: CdmEntityAttributeDefinition
        entity_attribute.entity = projection_entity_ref
        entity.attributes.append(entity_attribute)

        # Create resolution options with the 'referenceOnly' directive.
        res_opt = ResolveOptions(entity.in_document)
        res_opt.directives = AttributeResolutionDirectiveSet({'referenceOnly'})

        # Resolve the entity with 'referenceOnly'
        resolved_entity_with_reference_only = await entity.create_resolved_entity_async(
            'Resolved_{}.cdm.json'.format(entity.entity_name), res_opt,
            local_root)

        # Original set of attributes: ['id', 'name', 'value', 'date']
        # Condition not met, keep attributes in flat list
        self.assertEqual(4,
                         len(resolved_entity_with_reference_only.attributes))
        self.assertEqual(
            'id', resolved_entity_with_reference_only.attributes[0].name)
        self.assertEqual(
            'name', resolved_entity_with_reference_only.attributes[1].name)
        self.assertEqual(
            'value', resolved_entity_with_reference_only.attributes[2].name)
        self.assertEqual(
            'date', resolved_entity_with_reference_only.attributes[3].name)

        # Now resolve the entity with the 'structured' directive
        res_opt.directives = AttributeResolutionDirectiveSet({'structured'})
        resolved_entity_with_structured = await entity.create_resolved_entity_async(
            'Resolved_{}.cdm.json'.format(entity.entity_name), res_opt,
            local_root)

        # Original set of attributes: ['id', 'name', 'value', 'date']
        # Condition met, keep attributes in flat list and add the new attribute "newName" all attributes at the end
        self.assertEqual(5, len(resolved_entity_with_structured.attributes))
        self.assertEqual('id',
                         resolved_entity_with_structured.attributes[0].name)
        self.assertEqual('name',
                         resolved_entity_with_structured.attributes[1].name)
        self.assertEqual('value',
                         resolved_entity_with_structured.attributes[2].name)
        self.assertEqual('date',
                         resolved_entity_with_structured.attributes[3].name)
        self.assertEqual('newName',
                         resolved_entity_with_structured.attributes[4].name)
Example #11
0
    async def create_resolved_manifest_async(
        self, new_manifest_name: str, new_entity_document_name_format: str
    ) -> Optional['CdmManifestDefinition']:
        """Creates a resolved copy of the manifest.
        new_entity_document_name_format specifies a pattern to use when creating documents for resolved entities.
        The default is "resolved/{n}.cdm.json" to avoid a document name conflict with documents in the same folder as
        the manifest. Every instance of the string {n} is replaced with the entity name from the source manifest. Any
        sub-folders described by the pattern should exist in the corpus prior to calling this function.
        """

        if self.entities is None:
            return None

        if new_entity_document_name_format is None:
            new_entity_document_name_format = '{f}resolved/{n}.cdm.json'
        elif new_entity_document_name_format == '':  # for back compat
            new_entity_document_name_format = '{n}.cdm.json'
        elif '{n}' not in new_entity_document_name_format:  # for back compat
            new_entity_document_name_format = new_entity_document_name_format + '/{n}.cdm.json'

        source_manifest_path = self.ctx.corpus.storage.create_absolute_corpus_path(
            self.at_corpus_path, self)
        source_manifest_folder_path = self.ctx.corpus.storage.create_absolute_corpus_path(
            self.folder.at_corpus_path, self)

        resolved_manifest_path_split = new_manifest_name.rfind('/') + 1
        resolved_manifest_folder = None
        if resolved_manifest_path_split > 0:
            resolved_manifest_path = new_manifest_name[
                0:resolved_manifest_path_split]
            new_folder_path = self.ctx.corpus.storage.create_absolute_corpus_path(
                resolved_manifest_path, self)
            resolved_manifest_folder = await self.ctx.corpus.fetch_object_async(
                new_folder_path)  # type: CdmFolderDefinition
            if resolved_manifest_folder is None:
                self.ctx.logger.error(
                    'New folder for manifest not found {}'.format(
                        new_folder_path), 'create_resolved_manifest_async')
                return None
            new_manifest_name = new_manifest_name[
                resolved_manifest_path_split:]
        else:
            resolved_manifest_folder = self.owner

        self.ctx.logger.debug(
            'resolving manifest {}'.format(source_manifest_path),
            'create_resolved_manifest_async')

        # using the references present in the resolved entities, get an entity
        # create an imports doc with all the necessary resolved entity references and then resolve it
        resolved_manifest = CdmManifestDefinition(self.ctx, new_manifest_name)

        # add the new document to the folder
        if resolved_manifest_folder.documents.append(
                resolved_manifest) is None:
            # when would this happen?
            return None

        # mapping from entity path to resolved entity path for translating relationhsip paths
        res_ent_map = {}  # type: Dict[str, str]

        for entity in self.entities:
            ent_def = await self._get_entity_from_reference(entity, self)

            if not ent_def:
                self.ctx.logger.error('Unable to get entity from reference')
                return None

            # get the path from this manifest to the source entity. this will be the {f} replacement value
            source_entity_full_path = self.ctx.corpus.storage.create_absolute_corpus_path(
                ent_def.in_document.folder.at_corpus_path, self)
            f = ''
            if source_entity_full_path.startswith(source_manifest_folder_path):
                f = source_entity_full_path[len(source_manifest_folder_path):]

            new_document_full_path = new_entity_document_name_format.replace(
                '{n}', ent_def.entity_name).replace('{f}', f)
            new_document_full_path = self.ctx.corpus.storage.create_absolute_corpus_path(
                new_document_full_path, self)

            new_document_path_split = new_document_full_path.rfind('/') + 1
            new_document_path = new_document_full_path[
                0:new_document_path_split]
            new_document_name = new_document_full_path[
                new_document_path_split:]

            # make sure the new folder exists
            folder = await self.ctx.corpus.fetch_object_async(
                new_document_path)  # type: CdmFolderDefinition
            if not folder:
                self.ctx.logger.error(
                    'New folder not found {}'.format(new_document_path))
                return None

            # next create the resolved entity.
            res_opt = ResolveOptions()
            res_opt.wrt_doc = ent_def.in_document
            res_opt.directives = AttributeResolutionDirectiveSet(
                {'normalized', 'referenceOnly'})

            self.ctx.logger.debug(
                '    resolving entity {} to document {}'.format(
                    source_entity_full_path, new_document_full_path))

            resolved_entity = await ent_def.create_resolved_entity_async(
                ent_def.entity_name, res_opt, folder, new_document_name)
            if not resolved_entity:
                # fail all resolution, if any one entity resolution fails
                return None

            result = entity.copy(res_opt)
            if result.object_type == CdmObjectType.LOCAL_ENTITY_DECLARATION_DEF:
                relative_entity_path = self.ctx.corpus.storage.create_relative_corpus_path(
                    resolved_entity.at_corpus_path, resolved_manifest)
                result.entity_path = relative_entity_path or result.at_corpus_path

            resolved_manifest.entities.append(result)

            # absolute path is needed for generating relationships
            absolute_ent_path = self.ctx.corpus.storage.create_absolute_corpus_path(
                result.entity_path, resolved_manifest)
            res_ent_map[self.ctx.corpus.storage.create_absolute_corpus_path(
                ent_def.at_corpus_path,
                ent_def.in_document)] = absolute_ent_path

        self.ctx.logger.debug('    calculating relationships')
        # Calculate the entity graph for just this manifest.
        await self.ctx.corpus._calculate_entity_graph_async(
            resolved_manifest, res_ent_map)
        # Stick results into the relationships list for the manifest.
        await resolved_manifest.populate_manifest_relationships_async(
            CdmRelationshipDiscoveryStyle.EXCLUSIVE)

        # needed until Matt's changes with collections where I can propigate
        resolved_manifest._is_dirty = True
        return resolved_manifest
Example #12
0
    async def test_conditional_proj_using_object_model(self):
        """Test for creating a projection with an AddArtifactAttribute operation and a condition using the object model"""
        test_name = 'test_conditional_proj_using_object_model'
        corpus = ProjectionTestUtils.get_local_corpus(self.tests_subpath,
                                                      test_name)
        local_root = corpus.storage.fetch_root_folder('local')
        corpus.storage.mount('traitGroup',
                             LocalAdapter(self.trait_group_file_path))

        # Create an entity.
        entity = ProjectionTestUtils.create_entity(corpus, local_root)
        entity.in_document.imports.append('traitGroup:/TraitGroup.cdm.json')

        # Create a projection with a condition that states the operation should only execute when the resolution directive is 'structured'.
        projection = ProjectionTestUtils.create_projection(
            corpus, local_root)  # type: CdmProjection
        projection.condition = 'structured==true'
        projection.run_sequentially = True

        # Create an AlterTraits operation
        alter_traits_op_1 = corpus.make_object(
            CdmObjectType.OPERATION_ALTER_TRAITS_DEF
        )  # type: CdmOperationAlterTraits
        alter_traits_op_1.traits_to_add = CdmCollection(
            corpus.ctx, alter_traits_op_1, CdmObjectType.TRAIT_REF)
        alter_traits_op_1.traits_to_add.append(
            corpus.make_ref(CdmObjectType.TRAIT_REF, "means.TraitG100", True))
        alter_traits_op_1.traits_to_add.append(
            corpus.make_ref(CdmObjectType.TRAIT_GROUP_REF, "JobTitleBase",
                            True))
        alter_traits_op_1.traits_to_remove = CdmCollection(
            corpus.ctx, alter_traits_op_1, CdmObjectType.TRAIT_REF)
        alter_traits_op_1.traits_to_remove.append(
            corpus.make_ref(CdmObjectType.TRAIT_REF, "means.TraitG300", True))
        projection.operations.append(alter_traits_op_1)

        alter_traits_op_2 = corpus.make_object(
            CdmObjectType.OPERATION_ALTER_TRAITS_DEF
        )  # type: CdmOperationAlterTraits
        trait_g4 = corpus.make_ref(CdmObjectType.TRAIT_REF, "means.TraitG4",
                                   True)  # type: CdmTraitReference
        trait_g4.arguments.append('precision', '5')
        trait_g4.arguments.append('scale', '15')
        alter_traits_op_2.traits_to_add = CdmCollection(
            corpus.ctx, alter_traits_op_2, CdmObjectType.TRAIT_REF)
        alter_traits_op_2.traits_to_add.append(trait_g4)
        alter_traits_op_2.apply_to = []
        alter_traits_op_2.apply_to.append('name')
        projection.operations.append(alter_traits_op_2)

        # Create an entity reference to hold this projection.
        projection_entity_ref = corpus.make_object(
            CdmObjectType.ENTITY_REF, None)  # type: CdmEntityReference
        projection_entity_ref.explicit_reference = projection

        # Create an entity attribute that contains this projection and add this to the entity.
        entity_attribute = corpus.make_object(
            CdmObjectType.ENTITY_ATTRIBUTE_DEF,
            'TestEntityAttribute')  # type: CdmEntityAttributeDefinition
        entity_attribute.entity = projection_entity_ref
        entity.attributes.append(entity_attribute)

        # Create resolution options with the 'referenceOnly' directive.
        res_opt = ResolveOptions(entity.in_document)
        res_opt.directives = AttributeResolutionDirectiveSet({'referenceOnly'})

        # Resolve the entity with 'referenceOnly'
        resolved_entity_with_reference_only = await entity.create_resolved_entity_async(
            'Resolved_{}.cdm.json'.format(entity.entity_name), res_opt,
            local_root)

        # Original set of attributes: ['id', 'name', 'value', 'date']
        # Condition not met, no traits are added
        self.assertEqual(4,
                         len(resolved_entity_with_reference_only.attributes))
        self.validate_trait(resolved_entity_with_reference_only.attributes[0],
                            'id', False, True)
        self.validate_trait(resolved_entity_with_reference_only.attributes[1],
                            'name', False, True)
        self.validate_trait(resolved_entity_with_reference_only.attributes[2],
                            'value', False, True)
        self.validate_trait(resolved_entity_with_reference_only.attributes[3],
                            'date', False, True)

        # Now resolve the entity with the 'structured' directive
        res_opt.directives = AttributeResolutionDirectiveSet({'structured'})
        resolved_entity_with_structured = await entity.create_resolved_entity_async(
            'Resolved_{}.cdm.json'.format(entity.entity_name), res_opt,
            local_root)

        # Original set of attributes: ['id', 'name', 'value', 'date']
        # Condition met, new traits are added
        self.assertEqual(4, len(resolved_entity_with_structured.attributes))
        self.validate_trait(resolved_entity_with_structured.attributes[0],
                            'id')
        self.validate_trait(resolved_entity_with_structured.attributes[1],
                            'name', True)
        self.validate_trait(resolved_entity_with_structured.attributes[2],
                            'value')
        self.validate_trait(resolved_entity_with_structured.attributes[3],
                            'date')
Example #13
0
    async def run_test_with_values(
        self, test_name: str, source_entity_name: str,
        expected_context_default: 'AttributeContextExpectedValue',
        expected_context_normalized: 'AttributeContextExpectedValue',
        expected_context_reference_only: 'AttributeContextExpectedValue',
        expected_context_structured: 'AttributeContextExpectedValue',
        expected_context_normalized_structured: 'AttributeContextExpectedValue',
        expected_context_reference_only_normalized:
        'AttributeContextExpectedValue',
        expected_context_reference_only_structured:
        'AttributeContextExpectedValue',
        expected_context_reference_only_normalized_structured:
        'AttributeContextExpectedValue',
        expected_default: 'List[AttributeExpectedValue]',
        expected_normalized: 'List[AttributeExpectedValue]',
        expected_reference_only: 'List[AttributeExpectedValue]',
        expected_structured: 'List[AttributeExpectedValue]',
        expected_normalized_structured: 'List[AttributeExpectedValue]',
        expected_reference_only_normalized: 'List[AttributeExpectedValue]',
        expected_reference_only_structured: 'List[AttributeExpectedValue]',
        expected_reference_only_normalized_structured:
        'List[AttributeExpectedValue]'
    ) -> None:
        """This method runs the tests with a set expected attributes & attribute context values and validated the actual result."""
        try:
            test_input_path = TestHelper.get_input_folder_path(
                self.tests_subpath, test_name)
            test_actual_path = TestHelper.get_actual_output_folder_path(
                self.tests_subpath, test_name)
            test_expected_path = TestHelper.get_expected_output_folder_path(
                self.tests_subpath, test_name)
            corpus_path = test_input_path[0:(len(test_input_path) -
                                             len('/Input'))]
            test_actual_path = os.path.abspath(test_actual_path)

            corpus = CdmCorpusDefinition()
            corpus.ctx.report_at_level = CdmStatusLevel.WARNING
            corpus.storage.mount('local', LocalAdapter(corpus_path))
            corpus.storage.mount('cdm', LocalAdapter(self.schema_docs_path))
            corpus.storage.default_namespace = 'local'

            out_folder_path = corpus.storage.adapter_path_to_corpus_path(
                test_actual_path) + '/'  # interesting bug
            out_folder = await corpus.fetch_object_async(
                out_folder_path)  # type: CdmFolderDefinition

            src_entity_def = await corpus.fetch_object_async(
                'local:/Input/{}.cdm.json/{}'.format(source_entity_name,
                                                     source_entity_name)
            )  # type: CdmEntityDefinition
            self.assertTrue(src_entity_def is not None)

            res_opt = ResolveOptions(wrt_doc=src_entity_def.in_document)

            resolved_entity_def = None  # type: CdmEntityDefinition
            output_entity_name = ''
            output_entity_file_name = ''
            entity_file_name = ''

            if expected_context_default and expected_default:
                entity_file_name = 'd'
                res_opt.directives = AttributeResolutionDirectiveSet(set())
                output_entity_name = '{}_R_{}'.format(source_entity_name,
                                                      entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt, out_folder)
                await self.save_actual_entity_and_validate_with_expected(
                    os.path.join(test_expected_path, output_entity_file_name),
                    resolved_entity_def)

            if expected_context_normalized and expected_normalized:
                entity_file_name = 'n'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    {'normalized'})
                output_entity_name = '{}_R_{}'.format(source_entity_name,
                                                      entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt, out_folder)
                await self.save_actual_entity_and_validate_with_expected(
                    os.path.join(test_expected_path, output_entity_file_name),
                    resolved_entity_def)

            if expected_context_reference_only and expected_reference_only:
                entity_file_name = 'ro'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    {'referenceOnly'})
                output_entity_name = '{}_R_{}'.format(source_entity_name,
                                                      entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt, out_folder)
                await self.save_actual_entity_and_validate_with_expected(
                    os.path.join(test_expected_path, output_entity_file_name),
                    resolved_entity_def)

            if expected_context_structured and expected_structured:
                entity_file_name = 's'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    {'structured'})
                output_entity_name = '{}_R_{}'.format(source_entity_name,
                                                      entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt, out_folder)
                await self.save_actual_entity_and_validate_with_expected(
                    os.path.join(test_expected_path, output_entity_file_name),
                    resolved_entity_def)

            if expected_context_normalized_structured and expected_normalized_structured:
                entity_file_name = 'n_s'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    {'normalized', 'structured'})
                output_entity_name = '{}_R_{}'.format(source_entity_name,
                                                      entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt, out_folder)
                await self.save_actual_entity_and_validate_with_expected(
                    os.path.join(test_expected_path, output_entity_file_name),
                    resolved_entity_def)

            if expected_context_reference_only_normalized and expected_reference_only_normalized:
                entity_file_name = 'ro_n'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    {'referenceOnly', 'normalized'})
                output_entity_name = '{}_R_{}'.format(source_entity_name,
                                                      entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt, out_folder)
                await self.save_actual_entity_and_validate_with_expected(
                    os.path.join(test_expected_path, output_entity_file_name),
                    resolved_entity_def)

            if expected_context_reference_only_structured and expected_reference_only_structured:
                entity_file_name = 'ro_s'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    {'referenceOnly', 'structured'})
                output_entity_name = '{}_R_{}'.format(source_entity_name,
                                                      entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt, out_folder)
                await self.save_actual_entity_and_validate_with_expected(
                    os.path.join(test_expected_path, output_entity_file_name),
                    resolved_entity_def)

            if expected_context_reference_only_normalized_structured and expected_reference_only_normalized_structured:
                entity_file_name = 'ro_n_s'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    {'referenceOnly', 'normalized', 'structured'})
                output_entity_name = '{}_R_{}'.format(source_entity_name,
                                                      entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt, out_folder)
                await self.save_actual_entity_and_validate_with_expected(
                    os.path.join(test_expected_path, output_entity_file_name),
                    resolved_entity_def)
        except Exception as e:
            self.fail(e)
Example #14
0
    async def run_test_with_values(
        self, test_name: str, source_entity_name: str,
        expected_context_default: 'AttributeContextExpectedValue',
        expected_context_normalized: 'AttributeContextExpectedValue',
        expected_context_reference_only: 'AttributeContextExpectedValue',
        expected_context_structured: 'AttributeContextExpectedValue',
        expected_context_normalized_structured: 'AttributeContextExpectedValue',
        expected_context_reference_only_normalized:
        'AttributeContextExpectedValue',
        expected_context_reference_only_structured:
        'AttributeContextExpectedValue',
        expected_context_reference_only_normalized_structured:
        'AttributeContextExpectedValue',
        expected_default: 'List[AttributeExpectedValue]',
        expected_normalized: 'List[AttributeExpectedValue]',
        expected_reference_only: 'List[AttributeExpectedValue]',
        expected_structured: 'List[AttributeExpectedValue]',
        expected_normalized_structured: 'List[AttributeExpectedValue]',
        expected_reference_only_normalized: 'List[AttributeExpectedValue]',
        expected_reference_only_structured: 'List[AttributeExpectedValue]',
        expected_reference_only_normalized_structured:
        'List[AttributeExpectedValue]'
    ) -> None:
        """This method runs the tests with a set expected attributes & attribute context values and validated the actual result."""
        try:
            test_input_path = TestHelper.get_input_folder_path(
                self.tests_subpath, test_name)

            corpus = CdmCorpusDefinition()
            corpus.ctx.report_at_level = CdmStatusLevel.WARNING
            corpus.storage.mount('localInput', LocalAdapter(test_input_path))
            corpus.storage.mount('cdm', LocalAdapter(self.schema_docs_path))
            corpus.storage.default_namespace = 'localInput'

            src_entity_def = await corpus.fetch_object_async(
                'localInput:/{}.cdm.json/{}'.format(source_entity_name,
                                                    source_entity_name))
            self.assertTrue(src_entity_def is not None)

            res_opt = ResolveOptions(wrt_doc=src_entity_def.in_document)

            resolved_entity_def = None  # type: CdmEntityDefinition
            output_entity_name = ''
            output_entity_file_name = ''
            entity_file_name = ''

            if expected_context_default and expected_default:
                entity_file_name = 'default'
                res_opt.directives = AttributeResolutionDirectiveSet(set())
                output_entity_name = '{}_Resolved_{}'.format(
                    source_entity_name, entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt)
                self.validate_output_with_values(expected_context_default,
                                                 expected_default,
                                                 resolved_entity_def)

            if expected_context_normalized and expected_normalized:
                entity_file_name = 'normalized'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    set({'normalized'}))
                output_entity_name = '{}_Resolved_{}'.format(
                    source_entity_name, entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt)
                self.validate_output_with_values(expected_context_normalized,
                                                 expected_normalized,
                                                 resolved_entity_def)

            if expected_context_reference_only and expected_reference_only:
                entity_file_name = 'referenceOnly'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    set({'referenceOnly'}))
                output_entity_name = '{}_Resolved_{}'.format(
                    source_entity_name, entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt)
                self.validate_output_with_values(
                    expected_context_reference_only, expected_reference_only,
                    resolved_entity_def)

            if expected_context_structured and expected_structured:
                entity_file_name = 'structured'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    set({'structured'}))
                output_entity_name = '{}_Resolved_{}'.format(
                    source_entity_name, entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt)
                self.validate_output_with_values(expected_context_structured,
                                                 expected_structured,
                                                 resolved_entity_def)

            if expected_context_normalized_structured and expected_normalized_structured:
                entity_file_name = 'normalized_structured'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    set({'normalized', 'structured'}))
                output_entity_name = '{}_Resolved_{}'.format(
                    source_entity_name, entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt)
                self.validate_output_with_values(
                    expected_context_normalized_structured,
                    expected_normalized_structured, resolved_entity_def)

            if expected_context_reference_only_normalized and expected_reference_only_normalized:
                entity_file_name = 'referenceOnly_normalized'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    set({'referenceOnly', 'normalized'}))
                output_entity_name = '{}_Resolved_{}'.format(
                    source_entity_name, entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt)
                self.validate_output_with_values(
                    expected_context_reference_only_normalized,
                    expected_reference_only_normalized, resolved_entity_def)

            if expected_context_reference_only_structured and expected_reference_only_structured:
                entity_file_name = 'referenceOnly_structured'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    set({'referenceOnly', 'structured'}))
                output_entity_name = '{}_Resolved_{}'.format(
                    source_entity_name, entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt)
                self.validate_output_with_values(
                    expected_context_reference_only_structured,
                    expected_reference_only_structured, resolved_entity_def)

            if expected_context_reference_only_normalized_structured and expected_reference_only_normalized_structured:
                entity_file_name = 'referenceOnly_normalized_structured'
                res_opt.directives = AttributeResolutionDirectiveSet(
                    set({'referenceOnly', 'normalized', 'structured'}))
                output_entity_name = '{}_Resolved_{}'.format(
                    source_entity_name, entity_file_name)
                output_entity_file_name = '{}.cdm.json'.format(
                    output_entity_name)
                resolved_entity_def = await src_entity_def.create_resolved_entity_async(
                    output_entity_name, res_opt)
                self.validate_output_with_values(
                    expected_context_reference_only_normalized_structured,
                    expected_reference_only_normalized_structured,
                    resolved_entity_def)
        except Exception as e:
            self.fail(e)
Example #15
0
    async def test_conditional_proj_using_object_model(self):
        """Test for creating a projection with an ArrayExpansion operation and a condition using the object model"""
        corpus = TestHelper.get_local_corpus(self.tests_subpath, 'test_conditional_proj_using_object_model')
        corpus.storage.mount('local', LocalAdapter(TestHelper.get_actual_output_folder_path(self.tests_subpath, 'test_conditional_proj_using_object_model')))
        local_root = corpus.storage.fetch_root_folder('local')

        # Create an entity
        entity = ProjectionTestUtils.create_entity(corpus, local_root)

        # Create a projection with a condition that states the operation should only execute when the resolution directive is 'referenceOnly'
        projection = ProjectionTestUtils.create_projection(corpus, local_root)
        projection.condition = 'referenceOnly==True'

        # Create an ArrayExpansion operation
        array_expansion_op = corpus.make_object(CdmObjectType.OPERATION_ARRAY_EXPANSION_DEF)
        array_expansion_op.start_ordinal = 1
        array_expansion_op.end_ordinal = 2
        projection.operations.append(array_expansion_op)

        # Create an entity reference to hold this projection
        projection_entity_ref = corpus.make_object(CdmObjectType.ENTITY_REF, None)
        projection_entity_ref.explicit_reference = projection

        # Create another projection that does a rename so that we can see the expanded attributes in the final resolved entity
        projection2 = corpus.make_object(CdmObjectType.PROJECTION_DEF)
        projection2.source = projection_entity_ref

        # Create a RenameAttributes operation
        rename_attrs_op = corpus.make_object(CdmObjectType.OPERATION_RENAME_ATTRIBUTES_DEF)
        rename_attrs_op.rename_format = '{m}{o}'
        projection2.operations.append(rename_attrs_op)

        # Create an entity reference to hold this projection
        projection_entity_ref2 = corpus.make_object(CdmObjectType.ENTITY_REF, None)
        projection_entity_ref2.explicit_reference = projection2

        # Create an entity attribute that contains this projection and add this to the entity
        entity_attribute = corpus.make_object(CdmObjectType.ENTITY_ATTRIBUTE_DEF, 'TestEntityAttribute')
        entity_attribute.entity = projection_entity_ref2
        entity.attributes.append(entity_attribute)

        # Create resolution options with the 'referenceOnly' directive
        res_opt = ResolveOptions(entity.in_document)
        res_opt.directives = AttributeResolutionDirectiveSet(set(['referenceOnly']))

        # Resolve the entity with 'referenceOnly'
        resolved_entity_with_reference_only = await entity.create_resolved_entity_async('Resolved_{}.cdm.json'.format(entity.entity_name), res_opt, local_root)

        # Verify correctness of the resolved attributes after running the projections
        # Original set of attributes: ["id", "name", "value", "date"]
        # Expand 1...2, renameFormat = {m}{o}
        self.assertEqual(8, len(resolved_entity_with_reference_only.attributes))
        self.assertEqual('id1', resolved_entity_with_reference_only.attributes[0].name)
        self.assertEqual('name1', resolved_entity_with_reference_only.attributes[1].name)
        self.assertEqual('value1', resolved_entity_with_reference_only.attributes[2].name)
        self.assertEqual('date1', resolved_entity_with_reference_only.attributes[3].name)
        self.assertEqual('id2', resolved_entity_with_reference_only.attributes[4].name)
        self.assertEqual('name2', resolved_entity_with_reference_only.attributes[5].name)
        self.assertEqual('value2', resolved_entity_with_reference_only.attributes[6].name)
        self.assertEqual('date2', resolved_entity_with_reference_only.attributes[7].name)

        # Now resolve the entity with the default directives
        res_opt.directives = AttributeResolutionDirectiveSet(set([]))
        resolved_entity_with_structured = await entity.create_resolved_entity_async('Resolved_{}.cdm.json'.format(entity.entity_name), res_opt, local_root)

        # Verify correctness of the resolved attributes after running the projections
        # Original set of attributes: ["id", "name", "value", "date"]
        # Expand 1...2, renameFormat = {m}{o}
        self.assertEqual(4, len(resolved_entity_with_structured.attributes))
        self.assertEqual('id', resolved_entity_with_structured.attributes[0].name)
        self.assertEqual('name', resolved_entity_with_structured.attributes[1].name)
        self.assertEqual('value', resolved_entity_with_structured.attributes[2].name)
        self.assertEqual('date', resolved_entity_with_structured.attributes[3].name)
    async def run_test(self, test_name: str, source_entity_name: str) -> None:
        test_input_path = TestHelper.get_input_folder_path(self.tests_subpath, test_name)
        test_expected_output_path = TestHelper.get_expected_output_folder_path(self.tests_subpath, test_name)
        test_actual_output_path = TestHelper.get_actual_output_folder_path(self.tests_subpath, test_name)

        corpus = CdmCorpusDefinition()
        corpus.ctx.report_at_level = CdmStatusLevel.WARNING
        corpus.storage.mount('localInput', LocalAdapter(test_input_path))
        corpus.storage.mount('localExpectedOutput', LocalAdapter(test_expected_output_path))
        corpus.storage.mount('localActualOutput', LocalAdapter(test_actual_output_path))
        corpus.storage.mount('cdm', LocalAdapter(TestHelper.get_schema_docs_root()))
        corpus.storage.default_namespace = 'localInput'

        src_entity_def = await corpus.fetch_object_async('localInput:/{0}.cdm.json/{0}'.format(source_entity_name))  # type: CdmEntityDefinition
        self.assertIsNotNone(src_entity_def)

        res_opt = ResolveOptions(src_entity_def.in_document, directives=AttributeResolutionDirectiveSet(set()))

        actual_output_folder = await corpus.fetch_object_async('localActualOutput:/')  # type: CdmFolderDefinition
        resolved_entity_def = None
        output_entity_file_name = ''
        entity_file_name = ''

        entity_file_name = 'default'
        res_opt.directives = AttributeResolutionDirectiveSet(set())
        output_entity_file_name = '{}_Resolved_{}.cdm.json'.format(source_entity_name, entity_file_name)
        resolved_entity_def = await src_entity_def.create_resolved_entity_async(output_entity_file_name, res_opt, actual_output_folder)
        if await resolved_entity_def.in_document.save_as_async(output_entity_file_name, True):
            self.validate_output(output_entity_file_name, test_expected_output_path, test_actual_output_path)

        entity_file_name = 'referenceOnly'
        res_opt.directives = AttributeResolutionDirectiveSet({'referenceOnly'})
        output_entity_file_name = '{}_Resolved_{}.cdm.json'.format(source_entity_name, entity_file_name)
        resolved_entity_def = await src_entity_def.create_resolved_entity_async(output_entity_file_name, res_opt, actual_output_folder)
        if await resolved_entity_def.in_document.save_as_async(output_entity_file_name, True):
            self.validate_output(output_entity_file_name, test_expected_output_path, test_actual_output_path)

        entity_file_name = 'normalized'
        res_opt.directives = AttributeResolutionDirectiveSet({'normalized'})
        output_entity_file_name = '{}_Resolved_{}.cdm.json'.format(source_entity_name, entity_file_name)
        resolved_entity_def = await src_entity_def.create_resolved_entity_async(output_entity_file_name, res_opt, actual_output_folder)
        if await resolved_entity_def.in_document.save_as_async(output_entity_file_name, True):
            self.validate_output(output_entity_file_name, test_expected_output_path, test_actual_output_path)

        entity_file_name = 'structured'
        res_opt.directives = AttributeResolutionDirectiveSet({'structured'})
        output_entity_file_name = '{}_Resolved_{}.cdm.json'.format(source_entity_name, entity_file_name)
        resolved_entity_def = await src_entity_def.create_resolved_entity_async(output_entity_file_name, res_opt, actual_output_folder)
        if await resolved_entity_def.in_document.save_as_async(output_entity_file_name, True):
            self.validate_output(output_entity_file_name, test_expected_output_path, test_actual_output_path)

        entity_file_name = 'referenceOnly_normalized'
        res_opt.directives = AttributeResolutionDirectiveSet({'referenceOnly', 'normalized'})
        output_entity_file_name = '{}_Resolved_{}.cdm.json'.format(source_entity_name, entity_file_name)
        resolved_entity_def = await src_entity_def.create_resolved_entity_async(output_entity_file_name, res_opt, actual_output_folder)
        if await resolved_entity_def.in_document.save_as_async(output_entity_file_name, True):
            self.validate_output(output_entity_file_name, test_expected_output_path, test_actual_output_path)

        entity_file_name = 'referenceOnly_structured'
        res_opt.directives = AttributeResolutionDirectiveSet({'referenceOnly', 'structured'})
        output_entity_file_name = '{}_Resolved_{}.cdm.json'.format(source_entity_name, entity_file_name)
        resolved_entity_def = await src_entity_def.create_resolved_entity_async(output_entity_file_name, res_opt, actual_output_folder)
        if await resolved_entity_def.in_document.save_as_async(output_entity_file_name, True):
            self.validate_output(output_entity_file_name, test_expected_output_path, test_actual_output_path)

        entity_file_name = 'normalized_structured'
        res_opt.directives = AttributeResolutionDirectiveSet({'normalized', 'structured'})
        output_entity_file_name = '{}_Resolved_{}.cdm.json'.format(source_entity_name, entity_file_name)
        resolved_entity_def = await src_entity_def.create_resolved_entity_async(output_entity_file_name, res_opt, actual_output_folder)
        if await resolved_entity_def.in_document.save_as_async(output_entity_file_name, True):
            self.validate_output(output_entity_file_name, test_expected_output_path, test_actual_output_path)

        entity_file_name = 'referenceOnly_normalized_structured'
        res_opt.directives = AttributeResolutionDirectiveSet({'referenceOnly', 'normalized', 'structured'})
        output_entity_file_name = '{}_Resolved_{}.cdm.json'.format(source_entity_name, entity_file_name)
        resolved_entity_def = await src_entity_def.create_resolved_entity_async(output_entity_file_name, res_opt, actual_output_folder)
        if await resolved_entity_def.in_document.save_as_async(output_entity_file_name, True):
            self.validate_output(output_entity_file_name, test_expected_output_path, test_actual_output_path)
    async def logical_manipulation_using_projections(
            self, corpus: CdmCorpusDefinition):
        '''This sample demonstrates how to model a set of common scenarios using projections. 
        The projections feature provides a way to customize the definition of a logical entity by influencing how the entity is resolved by the object model.
        Here we will model three common use cases for using projections that are associated with the directives 'referenceOnly', 'structured' and 'normalized'.
        To get an overview of the projections feature as well as all of the supported operations refer to the link below.
        https://docs.microsoft.com/en-us/common-data-model/sdk/convert-logical-entities-resolved-entities#projection-overview
        '''
        print('Create logical entity definition.')

        logical_folder = await corpus.fetch_object_async(
            'output:/')  # type: CdmFolderDefinition

        logical_doc = logical_folder.documents.append('Person.cdm.json')
        logical_doc.imports.append('local:/Address.cdm.json')

        entity = logical_doc.definitions.append(
            'Person')  # type: CdmEntityDefinition

        # Add 'name' data typed attribute.
        name_attr = entity.attributes.append(
            'name')  # type: CdmTypeAttributeDefinition
        name_attr.data_type = CdmDataTypeReference(corpus.ctx, 'string', True)

        # Add 'age' data typed attribute.
        age_attr = entity.attributes.append(
            'age')  # type: CdmTypeAttributeDefinition
        age_attr.data_type = CdmDataTypeReference(corpus.ctx, 'string', True)

        # Add 'address' entity typed attribute.
        entity_attr = CdmEntityAttributeDefinition(corpus.ctx, 'address')
        entity_attr.entity = CdmEntityReference(corpus.ctx, 'Address', True)
        apply_array_expansion(entity_attr, 1, 3, '{m}{A}{o}', 'countAttribute')
        apply_default_behavior(entity_attr, 'addressFK', 'address')

        entity.attributes.append(entity_attr)

        # Add 'email' data typed attribute.
        email_attr = entity.attributes.append(
            'email')  # type: CdmTypeAttributeDefinition
        email_attr.data_type = CdmDataTypeReference(corpus.ctx, 'string', True)

        # Save the logical definition of Person.
        await entity.in_document.save_as_async('Person.cdm.json')

        print(
            'Get \'resolved\' folder where the resolved entities will be saved.'
        )

        resolved_folder = await corpus.fetch_object_async(
            'output:/')  # type: CdmFolderDefinition

        res_opt = ResolveOptions(entity)

        # To get more information about directives and their meaning refer to
        # https://docs.microsoft.com/en-us/common-data-model/sdk/convert-logical-entities-resolved-entities#directives-guidance-and-the-resulting-resolved-shapes

        # We will start by resolving this entity with the 'normalized' direcitve.
        # This directive will be used on this and the next two examples so we can analize the resolved entity
        # without the array expansion.
        print('Resolving logical entity with normalized directive.')
        res_opt.directives = AttributeResolutionDirectiveSet({'normalized'})
        res_normalized_entity = await entity.create_resolved_entity_async(
            f'normalized_{entity.entity_name}', res_opt, resolved_folder)
        await res_normalized_entity.in_document.save_as_async(
            f'{res_normalized_entity.entity_name}.cdm.json')

        # Another common scenario is to resolve an entity using the 'referenceOnly' directive.
        # This directives is used to replace the relationships with a foreign key.
        print('Resolving logical entity with referenceOnly directive.')
        res_opt.directives = AttributeResolutionDirectiveSet(
            {'normalized', 'referenceOnly'})
        res_reference_only_entity = await entity.create_resolved_entity_async(
            f'referenceOnly_{entity.entity_name}', res_opt, resolved_folder)
        await res_reference_only_entity.in_document.save_as_async(
            f'{res_reference_only_entity.entity_name}.cdm.json')

        # When dealing with structured data, like Json or parquet, it sometimes necessary to represent the idea that
        # a property can hold a complex object. The shape of the complex object is defined by the source entity pointed by the
        # entity attribute and we use the 'structured' directive to resolve the entity attribute as an attribute group.
        print('Resolving logical entity with structured directive.')
        res_opt.directives = AttributeResolutionDirectiveSet(
            {'normalized', 'structured'})
        res_structured_entity = await entity.create_resolved_entity_async(
            f'structured_{entity.entity_name}', res_opt, resolved_folder)
        await res_structured_entity.in_document.save_as_async(
            f'{res_structured_entity.entity_name}.cdm.json')

        # Now let us remove the 'normalized' directive so the array expansion operation can run.
        print('Resolving logical entity without directives (array expansion).')
        res_opt.directives = AttributeResolutionDirectiveSet({})
        res_array_entity = await entity.create_resolved_entity_async(
            f'array_expansion_{entity.entity_name}', res_opt, resolved_folder)
        await res_array_entity.in_document.save_as_async(
            f'{res_array_entity.entity_name}.cdm.json')