def __init__(self,
                 schema_template: SchemaTemplate,
                 header: str,
                 context_concrete_type,
                 context=None,
                 order_of_occurrence=1):
        # Context refers to the context in which the header is being specified for.
        # For example, the property `project.contributors.email` will have a slightly different
        # specification in the context of `project.contributors`, than in the context of `project`.
        # In the former, email does not have a multivalue parent, whereas it has in the latter.
        # Framing it differently, in the former, it is each of the `contributors` that's being defined;
        # in the latter it is the `project` that's being defined.

        if not context:
            context = context_concrete_type

        parent_field, *_ = utils.split_field_chain(header)
        parent_spec = self._map_key_to_spec(
            schema_template, parent_field) if parent_field != context else None

        field_spec = self._map_key_to_spec(schema_template, header)
        data_type = DataType.find(field_spec.get('value_type'))

        # concrete_type is the actual concrete type that the header represents. Particularly in cases
        # where the column represents a linking detail to another type, concrete_type is different from
        # context_concrete_type. concrete_type is the "inherent" type of the column whichever context
        # it is specified in.
        concrete_type = utils.extract_root_field(header)
        type_spec = self._map_key_to_spec(schema_template, concrete_type)
        domain_entity_from_schema = None
        if type_spec:
            schema = type_spec.get('schema')
            domain_entity_from_schema, *_ = schema.get('domain_entity').split(
                '/')

        self.field_name = header
        self.context_concrete_type = context_concrete_type
        self.domain_type = domain_entity_from_schema if domain_entity_from_schema else \
            ColumnSpecification.UNKNOWN_DOMAIN_TYPE
        self.data_type = data_type
        self.multivalue = field_spec.get('multivalue')
        self.multivalue_parent = (parent_spec
                                  and parent_spec.get('multivalue'))
        self.order_of_occurrence = order_of_occurrence
        self.entity_type = utils.extract_root_field(header)

        self.identity = field_spec.get('identifiable')
        self.external_reference = field_spec.get('external_reference')
 def build_raw(field_name,
               object_type,
               main_category,
               raw_spec,
               order_of_occurence=1,
               parent=None):
     data_type = DataType.find(raw_spec.get('value_type'))
     multivalue = bool(raw_spec.get('multivalue'))
     multivalue_parent = bool(
         parent.get('multivalue')) if parent != None else False
     identity: bool = bool(raw_spec.get('identifiable'))
     external_reference = bool(raw_spec.get('external_reference'))
     return ColumnSpecification(field_name,
                                object_type,
                                main_category,
                                data_type,
                                multivalue=multivalue,
                                multivalue_parent=multivalue_parent,
                                identity=identity,
                                external_reference=external_reference,
                                order_of_occurence=order_of_occurence)
 def test_find_not_found(self):
     # expect:
     self.assertEqual(DataType.UNDEFINED, DataType.find('does_not_exist'))
     self.assertEqual(DataType.UNDEFINED, DataType.find(None))
 def test_find_case_insensitive(self):
     # expect:
     self.assertEqual(DataType.STRING, DataType.find('string'))
     self.assertEqual(DataType.STRING, DataType.find('String'))
     self.assertEqual(DataType.STRING, DataType.find('STRING'))