def validate(self) -> bool: missing_fields = [] if not bool(self.name): missing_fields.append('name') if not bool(self.entity): missing_fields.append('entity') if bool(self.cardinality): if not bool(self.cardinality.minimum): missing_fields.append('cardinality.minimum') if not bool(self.cardinality.maximum): missing_fields.append('cardinality.maximum') if missing_fields: logger.error( self._TAG, self.ctx, Errors.validate_error_string(self.at_corpus_path, missing_fields)) return False if bool(self.cardinality): if not CardinalitySettings._is_minimum_valid( self.cardinality.minimum): logger.error( self._TAG, self.ctx, 'Invalid minimum cardinality {}.'.format( self.cardinality.minimum)) return False if not CardinalitySettings._is_maximum_valid( self.cardinality.maximum): logger.error( self._TAG, self.ctx, 'Invalid maximum cardinality {}.'.format( self.cardinality.maximum)) return False return True
def cardinality_settings_from_data(data: CardinalitySettings, attribute: CdmAttribute) -> CardinalitySettings: """Converts cardinality data into a CardinalitySettings object""" if data is None: return None cardinality = CardinalitySettings(attribute) cardinality.minimum = data.get('minimum') cardinality.maximum = data.get('maximum') if cardinality.minimum is not None and cardinality.maximum is not None: return cardinality else: return None return data
def __init__(self, res_opt: 'ResolveOptions', owner: 'CdmObjectDefinition', owner_ref: Optional['CdmObjectReference'] = None): # --- internal --- # Resolution option used self._res_opt = res_opt # type: ResolveOptions # The calling referencing EntityDef, the EntityAttributeDef, or the TypeAttributeDef that contains this projection self._owner = owner # type: CdmObjectDefinition # The EntityRef to the owning EntityDef, EntityAttributeDef, or TypeAttributeDef self._owner_ref = owner_ref # type: CdmObjectReference if owner and owner.object_type == CdmObjectType.ENTITY_ATTRIBUTE_DEF: # If EntityAttributeDef - then the Cardinality from the Owner EntityAttributeDef # This is ignored for EntityDef and will default to min:max = 0:1 self._cardinality = owner.cardinality if owner.cardinality else CardinalitySettings( owner) # type: CardinalitySettings # For entity attribute - get if the source is polymorphic self._is_source_polymorphic = owner.is_polymorphic_source is not None and owner.is_polymorphic_source == True # type: bool else: self._cardinality = None self._is_source_polymorphic = False # Is referenceOnly self._is_reference_only = res_opt.directives.has( 'referenceOnly' ) == True if res_opt.directives else False # type: bool # Is normalized self._is_normalized = res_opt.directives.has( 'normalized' ) == True if res_opt.directives else False # type: bool # Is structured self._is_structured = res_opt.directives.has( 'structured' ) == True if res_opt.directives else False # type: bool # Is virtual self._is_virtual = res_opt.directives.has( 'virtual') == True if res_opt.directives else False # type: bool # Has maximum depth override flag self._has_no_maximum_depth = res_opt.directives.has( 'noMaxDepth' ) == True if res_opt.directives else False # type: bool # Is array self._is_array = res_opt.directives.has( 'isArray' ) == True if res_opt.directives else False # type: Optional[bool] # if noMaxDepth directive the max depth is 32 else defaults to what was set by the user # these depths were arbitrary and were set for the resolution guidance # re-using the same for projections as well self._maximum_depth = DepthInfo.MAX_DEPTH_LIMIT if self._has_no_maximum_depth else res_opt.max_depth # type: Optional[int]
async def test_zero_minimum_cardinality(self): """ Test case scenario for Bug #23 from the projections internal bug bash Reference Link: https://commondatamodel.visualstudio.com/CDM/_workitems/edit/23 """ test_name = "test_zero_minimum_cardinality" corpus = TestHelper.get_local_corpus(self.tests_subpath, test_name) def callback(level, message): self.fail(message) corpus.set_event_callback(callback, CdmStatusLevel.WARNING) # Create Local Root Folder local_root = corpus.storage.fetch_root_folder('local') # Create Manifest manifest = corpus.make_object(CdmObjectType.MANIFEST_DEF, 'default') local_root.documents.append(manifest, 'default.manifest.cdm.json') entity_name = 'TestEntity' # Create Entity entity = corpus.make_object(CdmObjectType.ENTITY_DEF, entity_name) entity.extends_entity = corpus.make_ref(CdmObjectType.ENTITY_REF, 'CdmEntity', True) # Create Entity Document document = corpus.make_object(CdmObjectType.DOCUMENT_DEF, '{}.cdm.json'.format(entity_name), False) document.definitions.append(entity) local_root.documents.append(document, document.name) manifest.entities.append(entity) attribute_name = 'testAttribute' attribute_data_type = 'string' attribute_purpose = 'hasA' # Create Type Attribute attribute = corpus.make_object(CdmObjectType.TYPE_ATTRIBUTE_DEF, attribute_name, False) foo = attribute.is_nullable attribute.data_type = corpus.make_ref(CdmObjectType.DATA_TYPE_REF, attribute_data_type, True) attribute.purpose = corpus.make_ref(CdmObjectType.PURPOSE_REF, attribute_purpose, True) attribute.display_name = attribute_name if entity: entity.attributes.append(attribute) attribute.cardinality = CardinalitySettings(attribute) attribute.cardinality.minimum = '0' attribute.cardinality.maximum = '*' self.assertTrue(attribute.is_nullable)
def validate(self) -> bool: missing_fields = [] if not bool(self.name): missing_fields.append('name') if not bool(self.entity): missing_fields.append('entity') if bool(self.cardinality): if not bool(self.cardinality.minimum): missing_fields.append('cardinality.minimum') if not bool(self.cardinality.maximum): missing_fields.append('cardinality.maximum') if missing_fields: logger.error( self.ctx, self._TAG, 'validate', self.at_corpus_path, CdmLogCode.ERR_VALDN_INTEGRITY_CHECK_FAILURE, self.at_corpus_path, ', '.join(map(lambda s: '\'' + s + '\'', missing_fields))) return False if bool(self.cardinality): if not CardinalitySettings._is_minimum_valid( self.cardinality.minimum): logger.error(self.ctx, self._TAG, 'validate', self.at_corpus_path, CdmLogCode.ERR_VALDN_INVALID_MIN_CARDINALITY, self.cardinality.minimum) return False if not CardinalitySettings._is_maximum_valid( self.cardinality.maximum): logger.error(self.ctx, self._TAG, 'validate', self.at_corpus_path, CdmLogCode.ERR_VALDN_INVALID_MAX_CARDINALITY, self.cardinality.maximum) return False return True
def test_minimum(self): """Unit test for CardinalitySetting.IsMinimumValid""" test_name = 'TestMinimum' corpus = ProjectionTestUtils.get_local_corpus(self.tests_subpath, test_name) def callback(level, message): if 'CardinalitySettings | Invalid minimum cardinality -1.' not in message: self.fail('Some unexpected failure - {}!'.format(message)) corpus.set_event_callback(callback, CdmStatusLevel.WARNING) # Create Dummy Type Attribute attribute = corpus.make_object(CdmObjectType.TYPE_ATTRIBUTE_DEF, "dummyAttribute", False) attribute.cardinality = CardinalitySettings(attribute) attribute.cardinality.minimum = '-1'
def from_data( ctx: CdmCorpusContext, data: TypeAttribute, entity_name: Optional[str] = None) -> CdmTypeAttributeDefinition: type_attribute = ctx.corpus.make_object( CdmObjectType.TYPE_ATTRIBUTE_DEF, data.get('name')) type_attribute.purpose = PurposeReferencePersistence.from_data( ctx, data.get('purpose')) type_attribute.data_type = DataTypeReferencePersistence.from_data( ctx, data.get('dataType')) if data.get('cardinality'): min_cardinality = None if data.get('cardinality').get('minimum'): min_cardinality = data.get('cardinality').get('minimum') max_cardinality = None if data.get('cardinality').get('maximum'): max_cardinality = data.get('cardinality').get('maximum') if not min_cardinality or not max_cardinality: logger.error( _TAG, ctx, 'Both minimum and maximum are required for the Cardinality property.' ) if not CardinalitySettings._is_minimum_valid(min_cardinality): logger.error( _TAG, ctx, 'Invalid minimum cardinality {}.'.format(min_cardinality)) if not CardinalitySettings._is_maximum_valid(max_cardinality): logger.error( _TAG, ctx, 'Invalid maximum cardinality {}.'.format(max_cardinality)) if min_cardinality and max_cardinality and CardinalitySettings._is_minimum_valid( min_cardinality) and CardinalitySettings._is_maximum_valid( max_cardinality): type_attribute.cardinality = CardinalitySettings( type_attribute) type_attribute.cardinality.minimum = min_cardinality type_attribute.cardinality.maximum = max_cardinality type_attribute.attribute_context = AttributeContextReferencePersistence.from_data( ctx, data.get('attributeContext')) type_attribute.resolution_guidance = AttributeResolutionGuidancePersistence.from_data( ctx, data.get('resolutionGuidance')) applied_traits = utils.create_trait_reference_array( ctx, data.get('appliedTraits')) type_attribute.applied_traits.extend(applied_traits) if data.get('isPrimaryKey') and entity_name: t2p_map = TraitToPropertyMap(type_attribute) t2p_map._update_property_value( 'isPrimaryKey', entity_name + '/(resolvedAttributes)/' + type_attribute.name) type_attribute.explanation = data.explanation type_attribute.is_read_only = utils._property_from_data_to_bool( data.isReadOnly) type_attribute.is_nullable = utils._property_from_data_to_bool( data.isNullable) type_attribute.source_name = utils._property_from_data_to_string( data.sourceName) type_attribute.source_ordering = utils._property_from_data_to_int( data.sourceOrdering) type_attribute.display_name = utils._property_from_data_to_string( data.displayName) type_attribute.description = utils._property_from_data_to_string( data.description) type_attribute.value_constrained_to_list = utils._property_from_data_to_bool( data.valueConstrainedToList) type_attribute.maximum_length = utils._property_from_data_to_int( data.maximumLength) type_attribute.maximum_value = utils._property_from_data_to_string( data.maximumValue) type_attribute.minimum_value = utils._property_from_data_to_string( data.minimumValue) type_attribute.default_value = data.defaultValue type_attribute.projection = ProjectionPersistence.from_data( ctx, data.projection) if data.get('dataFormat') is not None: try: type_attribute.data_format = TypeAttributePersistence._data_type_from_data( data.dataFormat) except ValueError: logger.warning( TypeAttributePersistence.__name__, ctx, 'Couldn\'t find an enum value for {}.'.format( data.dataFormat), TypeAttributePersistence.from_data.__name__) return type_attribute
def from_data(ctx: CdmCorpusContext, data: EntityAttribute) -> CdmEntityAttributeDefinition: entity_attribute = ctx.corpus.make_object( CdmObjectType.ENTITY_ATTRIBUTE_DEF, data.name) entity_attribute.description = data.description entity_attribute.display_name = data.displayName entity_attribute.explanation = data.explanation if data.get('cardinality'): min_cardinality = None if data.get('cardinality').get('minimum'): min_cardinality = data.get('cardinality').get('minimum') max_cardinality = None if data.get('cardinality').get('maximum'): max_cardinality = data.get('cardinality').get('maximum') if not min_cardinality or not max_cardinality: logger.error(ctx, _TAG, EntityAttributePersistence.from_data.__name__, None, CdmLogCode.ERR_PERSIST_CARDINALITY_PROP_MISSING) if not CardinalitySettings._is_minimum_valid(min_cardinality): logger.error(ctx, _TAG, EntityAttributePersistence.from_data.__name__, None, CdmLogCode.ERR_PERSIST_INVALID_MIN_CARDINALITY, min_cardinality) if not CardinalitySettings._is_maximum_valid(max_cardinality): logger.error(ctx, _TAG, EntityAttributePersistence.from_data.__name__, None, CdmLogCode.ERR_PERSIST_INVALID_MAX_CARDINALITY, max_cardinality) if min_cardinality and max_cardinality and CardinalitySettings._is_minimum_valid( min_cardinality) and CardinalitySettings._is_maximum_valid( max_cardinality): entity_attribute.cardinality = CardinalitySettings( entity_attribute) entity_attribute.cardinality.minimum = min_cardinality entity_attribute.cardinality.maximum = max_cardinality entity_attribute.is_polymorphic_source = data.get( 'isPolymorphicSource') is_projection = data.get('entity') and not isinstance( data.get('entity'), str) and data.get('entity').get('source') if is_projection: projection = ProjectionPersistence.from_data( ctx, data.get('entity')) projection.owner = entity_attribute inline_entity_ref = ctx.corpus.make_object( CdmObjectType.ENTITY_REF, None) inline_entity_ref.explicit_reference = projection entity_attribute.entity = inline_entity_ref else: entity_attribute.entity = EntityReferencePersistence.from_data( ctx, data.get('entity')) entity_attribute.purpose = PurposeReferencePersistence.from_data( ctx, data.get('purpose')) utils.add_list_to_cdm_collection( entity_attribute.applied_traits, utils.create_trait_reference_array(ctx, data.get('appliedTraits'))) # Ignore resolution guidance if the entity is a projection if data.get('resolutionGuidance') and is_projection: logger.error(ctx, _TAG, 'from_data', None, CdmLogCode.ERR_PERSIST_ENTITY_ATTR_UNSUPPORTED, entity_attribute.name) else: entity_attribute.resolution_guidance = AttributeResolutionGuidancePersistence.from_data( ctx, data.get('resolutionGuidance')) return entity_attribute
def from_data( ctx: CdmCorpusContext, data: TypeAttribute, entity_name: Optional[str] = None) -> CdmTypeAttributeDefinition: type_attribute = ctx.corpus.make_object( CdmObjectType.TYPE_ATTRIBUTE_DEF, data.get('name')) type_attribute.purpose = PurposeReferencePersistence.from_data( ctx, data.get('purpose')) type_attribute.data_type = DataTypeReferencePersistence.from_data( ctx, data.get('dataType')) if data.get('cardinality'): min_cardinality = None if data.get('cardinality').get('minimum'): min_cardinality = data.get('cardinality').get('minimum') max_cardinality = None if data.get('cardinality').get('maximum'): max_cardinality = data.get('cardinality').get('maximum') if not min_cardinality or not max_cardinality: logger.error(ctx, _TAG, 'from_data', None, CdmLogCode.ERR_PERSIST_CARDINALITY_PROP_MISSING) if not CardinalitySettings._is_minimum_valid(min_cardinality): logger.error(ctx, _TAG, 'from_data', None, CdmLogCode.ERR_VALDN_INVALID_MIN_CARDINALITY, min_cardinality) if not CardinalitySettings._is_maximum_valid(max_cardinality): logger.error(ctx, _TAG, 'from_data', None, CdmLogCode.ERR_VALDN_INVALID_MAX_CARDINALITY, max_cardinality) if min_cardinality and max_cardinality and CardinalitySettings._is_minimum_valid( min_cardinality) and CardinalitySettings._is_maximum_valid( max_cardinality): type_attribute.cardinality = CardinalitySettings( type_attribute) type_attribute.cardinality.minimum = min_cardinality type_attribute.cardinality.maximum = max_cardinality type_attribute.attribute_context = AttributeContextReferencePersistence.from_data( ctx, data.get('attributeContext')) utils.add_list_to_cdm_collection( type_attribute.applied_traits, utils.create_trait_reference_array(ctx, data.get('appliedTraits'))) type_attribute.resolution_guidance = AttributeResolutionGuidancePersistence.from_data( ctx, data.get('resolutionGuidance')) if data.get('isPrimaryKey') and entity_name: t2p_map = TraitToPropertyMap(type_attribute) t2p_map._update_property_value( 'isPrimaryKey', entity_name + '/(resolvedAttributes)/' + type_attribute.name) type_attribute.explanation = data.explanation type_attribute.is_read_only = utils._property_from_data_to_bool( data.isReadOnly) type_attribute.is_nullable = utils._property_from_data_to_bool( data.isNullable) type_attribute.source_name = utils._property_from_data_to_string( data.sourceName) type_attribute.source_ordering = utils._property_from_data_to_int( data.sourceOrdering) type_attribute.display_name = utils._property_from_data_to_string( data.displayName) type_attribute.description = utils._property_from_data_to_string( data.description) type_attribute.value_constrained_to_list = utils._property_from_data_to_bool( data.valueConstrainedToList) type_attribute.maximum_length = utils._property_from_data_to_int( data.maximumLength) type_attribute.maximum_value = utils._property_from_data_to_string( data.maximumValue) type_attribute.minimum_value = utils._property_from_data_to_string( data.minimumValue) type_attribute.default_value = data.defaultValue type_attribute.projection = ProjectionPersistence.from_data( ctx, data.projection) if data.get('dataFormat') is not None: try: type_attribute.data_format = TypeAttributePersistence._data_type_from_data( data.dataFormat) except ValueError: logger.warning(ctx, _TAG, TypeAttributePersistence.from_data.__name__, None, CdmLogCode.WARN_PERSIST_ENUM_NOT_FOUND, data.dataFormat) return type_attribute
def from_data(ctx: CdmCorpusContext, data: EntityAttribute) -> CdmEntityAttributeDefinition: entity_attribute = ctx.corpus.make_object( CdmObjectType.ENTITY_ATTRIBUTE_DEF, data.name) entity_attribute.description = data.description entity_attribute.display_name = data.displayName entity_attribute.explanation = data.explanation if data.get('cardinality'): min_cardinality = None if data.get('cardinality').get('minimum'): min_cardinality = data.get('cardinality').get('minimum') max_cardinality = None if data.get('cardinality').get('maximum'): max_cardinality = data.get('cardinality').get('maximum') if not min_cardinality or not max_cardinality: logger.error( _TAG, ctx, 'Both minimum and maximum are required for the Cardinality property.' ) if not CardinalitySettings._is_minimum_valid(min_cardinality): logger.error( _TAG, ctx, 'Invalid minimum cardinality {}.'.format(min_cardinality)) if not CardinalitySettings._is_maximum_valid(max_cardinality): logger.error( _TAG, ctx, 'Invalid maximum cardinality {}.'.format(max_cardinality)) if min_cardinality and max_cardinality and CardinalitySettings._is_minimum_valid( min_cardinality) and CardinalitySettings._is_maximum_valid( max_cardinality): entity_attribute.cardinality = CardinalitySettings( entity_attribute) entity_attribute.cardinality.minimum = min_cardinality entity_attribute.cardinality.maximum = max_cardinality entity_attribute.is_polymorphic_source = data.get( 'isPolymorphicSource') is_projection = data.get('entity') and not isinstance( data.get('entity'), str) and data.get('entity').get('source') if is_projection: projection = ProjectionPersistence.from_data( ctx, data.get('entity')) projection.owner = entity_attribute inline_entity_ref = ctx.corpus.make_object( CdmObjectType.ENTITY_REF, None) inline_entity_ref.explicit_reference = projection entity_attribute.entity = inline_entity_ref else: entity_attribute.entity = EntityReferencePersistence.from_data( ctx, data.get('entity')) entity_attribute.purpose = PurposeReferencePersistence.from_data( ctx, data.get('purpose')) applied_traits = utils.create_trait_reference_array( ctx, data.get('appliedTraits')) entity_attribute.applied_traits.extend(applied_traits) # Ignore resolution guidance if the entity is a projection if data.get('resolutionGuidance') and is_projection: logger.error( _TAG, ctx, 'The EntityAttribute {} is projection based. Resolution guidance is not supported with a projection.' .format(entity_attribute.name)) else: entity_attribute.resolution_guidance = AttributeResolutionGuidancePersistence.from_data( ctx, data.get('resolutionGuidance')) return entity_attribute