mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-31 02:29:03 +00:00 
			
		
		
		
	This commit is contained in:
		
							parent
							
								
									581f63b157
								
							
						
					
					
						commit
						b0d64d1684
					
				| @ -21,6 +21,8 @@ import java.util.concurrent.ConcurrentHashMap; | |||||||
| import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||||
| import org.openmetadata.catalog.entity.Type; | import org.openmetadata.catalog.entity.Type; | ||||||
| import org.openmetadata.catalog.entity.type.CustomProperty; | import org.openmetadata.catalog.entity.type.CustomProperty; | ||||||
|  | import org.openmetadata.catalog.exception.CatalogExceptionMessage; | ||||||
|  | import org.openmetadata.catalog.exception.EntityNotFoundException; | ||||||
| import org.openmetadata.catalog.util.FullyQualifiedName; | import org.openmetadata.catalog.util.FullyQualifiedName; | ||||||
| import org.openmetadata.catalog.util.JsonUtils; | import org.openmetadata.catalog.util.JsonUtils; | ||||||
| 
 | 
 | ||||||
| @ -48,7 +50,6 @@ public class TypeRegistry { | |||||||
| 
 | 
 | ||||||
|   public void addType(Type type) { |   public void addType(Type type) { | ||||||
|     TYPES.put(type.getName(), type); |     TYPES.put(type.getName(), type); | ||||||
|     LOG.info("Updated type {}\n", type.getName()); |  | ||||||
| 
 | 
 | ||||||
|     // Store custom properties added to a type |     // Store custom properties added to a type | ||||||
|     for (CustomProperty property : type.getCustomProperties()) { |     for (CustomProperty property : type.getCustomProperties()) { | ||||||
| @ -58,18 +59,17 @@ public class TypeRegistry { | |||||||
| 
 | 
 | ||||||
|   public void removeType(String typeName) { |   public void removeType(String typeName) { | ||||||
|     TYPES.remove(typeName); |     TYPES.remove(typeName); | ||||||
|     LOG.info("Deleted type {}\n", typeName); |     LOG.info("Deleted type {}", typeName); | ||||||
|     // TODO cleanup custom properties |     // TODO cleanup custom properties | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private void addCustomProperty(String entityType, String propertyName, CustomProperty customProperty) { |   private void addCustomProperty(String entityType, String propertyName, CustomProperty customProperty) { | ||||||
|     String customPropertyFQN = getCustomPropertyFQN(entityType, propertyName); |     String customPropertyFQN = getCustomPropertyFQN(entityType, propertyName); | ||||||
|     CUSTOM_PROPERTIES.put(customPropertyFQN, customProperty); |     CUSTOM_PROPERTIES.put(customPropertyFQN, customProperty); | ||||||
|     LOG.info("Adding custom property {}\n", customPropertyFQN); |  | ||||||
| 
 | 
 | ||||||
|     JsonSchema jsonSchema = JsonUtils.getJsonSchema(TYPES.get(customProperty.getPropertyType().getName()).getSchema()); |     JsonSchema jsonSchema = JsonUtils.getJsonSchema(TYPES.get(customProperty.getPropertyType().getName()).getSchema()); | ||||||
|     CUSTOM_PROPERTY_SCHEMAS.put(customPropertyFQN, jsonSchema); |     CUSTOM_PROPERTY_SCHEMAS.put(customPropertyFQN, jsonSchema); | ||||||
|     LOG.info("Adding json schema for {} {}\n", customProperty, jsonSchema); |     LOG.info("Adding custom property {} with JSON schema {}", customPropertyFQN, jsonSchema); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public JsonSchema getSchema(String entityType, String propertyName) { |   public JsonSchema getSchema(String entityType, String propertyName) { | ||||||
| @ -81,9 +81,8 @@ public class TypeRegistry { | |||||||
|     // Validate custom properties added to a type |     // Validate custom properties added to a type | ||||||
|     for (CustomProperty property : listOrEmpty(type.getCustomProperties())) { |     for (CustomProperty property : listOrEmpty(type.getCustomProperties())) { | ||||||
|       if (TYPES.get(property.getPropertyType().getName()) == null) { |       if (TYPES.get(property.getPropertyType().getName()) == null) { | ||||||
|         throw new IllegalArgumentException( |         throw EntityNotFoundException.byMessage( | ||||||
|             String.format( |             CatalogExceptionMessage.entityNotFound(Entity.TYPE, property.getPropertyType().getId())); | ||||||
|                 "Type %s not found for custom property %s", property.getPropertyType().getName(), property.getName())); |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -87,11 +87,6 @@ public class TypeRepository extends EntityRepository<Type> { | |||||||
|     TypeRegistry.instance().removeType(entity.getName()); |     TypeRegistry.instance().removeType(entity.getName()); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |  | ||||||
|   protected void postUpdate(Type entity) { |  | ||||||
|     TypeRegistry.instance().addType(entity); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   @Override |   @Override | ||||||
|   public EntityUpdater getUpdater(Type original, Type updated, Operation operation) { |   public EntityUpdater getUpdater(Type original, Type updated, Operation operation) { | ||||||
|     return new TypeUpdater(original, updated, operation); |     return new TypeUpdater(original, updated, operation); | ||||||
| @ -102,7 +97,7 @@ public class TypeRepository extends EntityRepository<Type> { | |||||||
|     Type type = dao.findEntityById(UUID.fromString(id), Include.NON_DELETED); |     Type type = dao.findEntityById(UUID.fromString(id), Include.NON_DELETED); | ||||||
|     property.setPropertyType(dao.findEntityReferenceById(property.getPropertyType().getId(), Include.NON_DELETED)); |     property.setPropertyType(dao.findEntityReferenceById(property.getPropertyType().getId(), Include.NON_DELETED)); | ||||||
|     if (type.getCategory().equals(Category.Field)) { |     if (type.getCategory().equals(Category.Field)) { | ||||||
|       throw new IllegalArgumentException("Property types can't be extended"); |       throw new IllegalArgumentException("Only entity types can be extended and field types can't be extended"); | ||||||
|     } |     } | ||||||
|     setFields(type, putFields); |     setFields(type, putFields); | ||||||
| 
 | 
 | ||||||
| @ -148,7 +143,7 @@ public class TypeRepository extends EntityRepository<Type> { | |||||||
|       List<CustomProperty> origFields = listOrEmpty(original.getCustomProperties()); |       List<CustomProperty> origFields = listOrEmpty(original.getCustomProperties()); | ||||||
|       List<CustomProperty> added = new ArrayList<>(); |       List<CustomProperty> added = new ArrayList<>(); | ||||||
|       List<CustomProperty> deleted = new ArrayList<>(); |       List<CustomProperty> deleted = new ArrayList<>(); | ||||||
|       recordListChange("customProperty", origFields, updatedFields, added, deleted, EntityUtil.customFieldMatch); |       recordListChange("customProperties", origFields, updatedFields, added, deleted, EntityUtil.customFieldMatch); | ||||||
|       for (CustomProperty property : added) { |       for (CustomProperty property : added) { | ||||||
|         String customPropertyFQN = getCustomPropertyFQN(updated.getName(), property.getName()); |         String customPropertyFQN = getCustomPropertyFQN(updated.getName(), property.getName()); | ||||||
|         String customPropertyJson = JsonUtils.pojoToJson(property); |         String customPropertyJson = JsonUtils.pojoToJson(property); | ||||||
|  | |||||||
| @ -56,6 +56,7 @@ import org.openmetadata.catalog.CatalogApplicationConfig; | |||||||
| import org.openmetadata.catalog.Entity; | import org.openmetadata.catalog.Entity; | ||||||
| import org.openmetadata.catalog.api.CreateType; | import org.openmetadata.catalog.api.CreateType; | ||||||
| import org.openmetadata.catalog.entity.Type; | import org.openmetadata.catalog.entity.Type; | ||||||
|  | import org.openmetadata.catalog.entity.type.Category; | ||||||
| import org.openmetadata.catalog.entity.type.CustomProperty; | import org.openmetadata.catalog.entity.type.CustomProperty; | ||||||
| import org.openmetadata.catalog.jdbi3.CollectionDAO; | import org.openmetadata.catalog.jdbi3.CollectionDAO; | ||||||
| import org.openmetadata.catalog.jdbi3.ListFilter; | import org.openmetadata.catalog.jdbi3.ListFilter; | ||||||
| @ -66,6 +67,7 @@ import org.openmetadata.catalog.security.Authorizer; | |||||||
| import org.openmetadata.catalog.security.SecurityUtil; | import org.openmetadata.catalog.security.SecurityUtil; | ||||||
| import org.openmetadata.catalog.type.EntityHistory; | import org.openmetadata.catalog.type.EntityHistory; | ||||||
| import org.openmetadata.catalog.type.Include; | import org.openmetadata.catalog.type.Include; | ||||||
|  | import org.openmetadata.catalog.util.EntityUtil.Fields; | ||||||
| import org.openmetadata.catalog.util.JsonUtils; | import org.openmetadata.catalog.util.JsonUtils; | ||||||
| import org.openmetadata.catalog.util.RestUtil.PutResponse; | import org.openmetadata.catalog.util.RestUtil.PutResponse; | ||||||
| import org.openmetadata.catalog.util.ResultList; | import org.openmetadata.catalog.util.ResultList; | ||||||
| @ -92,7 +94,7 @@ public class TypeResource extends EntityResource<Type, TypeRepository> { | |||||||
| 
 | 
 | ||||||
|   @SuppressWarnings("unused") // Method used for reflection |   @SuppressWarnings("unused") // Method used for reflection | ||||||
|   public void initialize(CatalogApplicationConfig config) throws IOException { |   public void initialize(CatalogApplicationConfig config) throws IOException { | ||||||
|     // Find tag definitions and load tag categories from the json file, if necessary |     // Load types defined in OpenMetadata schemas | ||||||
|     long now = System.currentTimeMillis(); |     long now = System.currentTimeMillis(); | ||||||
|     List<Type> types = JsonUtils.getTypes(); |     List<Type> types = JsonUtils.getTypes(); | ||||||
|     types.forEach( |     types.forEach( | ||||||
| @ -100,6 +102,17 @@ public class TypeResource extends EntityResource<Type, TypeRepository> { | |||||||
|           type.withId(UUID.randomUUID()).withUpdatedBy("admin").withUpdatedAt(now); |           type.withId(UUID.randomUUID()).withUpdatedBy("admin").withUpdatedAt(now); | ||||||
|           LOG.info("Loading type {}", type.getName()); |           LOG.info("Loading type {}", type.getName()); | ||||||
|           try { |           try { | ||||||
|  |             Fields fields = getFields("customProperties"); | ||||||
|  |             try { | ||||||
|  |               Type storedType = dao.getByName(null, type.getName(), fields); | ||||||
|  |               type.setId(storedType.getId()); | ||||||
|  |               // If entity type already exists, then carry forward custom properties | ||||||
|  |               if (storedType.getCategory().equals(Category.Entity)) { | ||||||
|  |                 type.setCustomProperties(storedType.getCustomProperties()); | ||||||
|  |               } | ||||||
|  |             } catch (Exception e) { | ||||||
|  |               LOG.debug("Creating entity that does not exist ", e); | ||||||
|  |             } | ||||||
|             this.dao.createOrUpdate(null, type); |             this.dao.createOrUpdate(null, type); | ||||||
|           } catch (IOException e) { |           } catch (IOException e) { | ||||||
|             LOG.error("Error loading type {}", type.getName(), e); |             LOG.error("Error loading type {}", type.getName(), e); | ||||||
|  | |||||||
| @ -159,7 +159,7 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|   protected boolean supportsFieldsQueryParam = true; |   protected boolean supportsFieldsQueryParam = true; | ||||||
|   protected boolean supportsEmptyDescription = true; |   protected boolean supportsEmptyDescription = true; | ||||||
|   protected boolean supportsNameWithDot = true; |   protected boolean supportsNameWithDot = true; | ||||||
|   protected final boolean supportsCustomAttributes; |   protected final boolean supportsCustomExtension; | ||||||
| 
 | 
 | ||||||
|   public static final String DATA_STEWARD_ROLE_NAME = "DataSteward"; |   public static final String DATA_STEWARD_ROLE_NAME = "DataSteward"; | ||||||
|   public static final String DATA_CONSUMER_ROLE_NAME = "DataConsumer"; |   public static final String DATA_CONSUMER_ROLE_NAME = "DataConsumer"; | ||||||
| @ -248,7 +248,7 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|     this.supportsOwner = allowedFields.contains(FIELD_OWNER); |     this.supportsOwner = allowedFields.contains(FIELD_OWNER); | ||||||
|     this.supportsTags = allowedFields.contains(FIELD_TAGS); |     this.supportsTags = allowedFields.contains(FIELD_TAGS); | ||||||
|     this.supportsSoftDelete = allowedFields.contains(FIELD_DELETED); |     this.supportsSoftDelete = allowedFields.contains(FIELD_DELETED); | ||||||
|     this.supportsCustomAttributes = allowedFields.contains(FIELD_EXTENSION); |     this.supportsCustomExtension = allowedFields.contains(FIELD_EXTENSION); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @BeforeAll |   @BeforeAll | ||||||
| @ -1130,38 +1130,55 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
|   void put_entityExtension(TestInfo test) throws IOException { |   void put_addEntityCustomAttributes(TestInfo test) throws IOException { | ||||||
|     if (!supportsCustomAttributes) { |     if (!supportsCustomExtension) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Add valid custom fields to the entity |     // PUT valid custom field intA to the entity type | ||||||
|     TypeResourceTest typeResourceTest = new TypeResourceTest(); |     TypeResourceTest typeResourceTest = new TypeResourceTest(); | ||||||
|     INT_TYPE = typeResourceTest.getEntityByName("integer", "", ADMIN_AUTH_HEADERS); |     INT_TYPE = typeResourceTest.getEntityByName("integer", "", ADMIN_AUTH_HEADERS); | ||||||
|     STRING_TYPE = typeResourceTest.getEntityByName("string", "", ADMIN_AUTH_HEADERS); |     STRING_TYPE = typeResourceTest.getEntityByName("string", "", ADMIN_AUTH_HEADERS); | ||||||
|     Type entity = typeResourceTest.getEntityByName(this.entityType, "customProperties", ADMIN_AUTH_HEADERS); |     Type entity = typeResourceTest.getEntityByName(this.entityType, "customProperties", ADMIN_AUTH_HEADERS); | ||||||
|     CustomProperty fieldA = |     CustomProperty fieldA = | ||||||
|         new CustomProperty().withName("intA").withDescription("intA").withPropertyType(INT_TYPE.getEntityReference()); |         new CustomProperty().withName("intA").withDescription("intA").withPropertyType(INT_TYPE.getEntityReference()); | ||||||
|  |     entity = typeResourceTest.addAndCheckCustomProperty(entity.getId(), fieldA, OK, ADMIN_AUTH_HEADERS); | ||||||
|  |     final UUID id = entity.getId(); | ||||||
|  | 
 | ||||||
|  |     // PATCH valid custom field stringB | ||||||
|     CustomProperty fieldB = |     CustomProperty fieldB = | ||||||
|         new CustomProperty() |         new CustomProperty() | ||||||
|             .withName("stringB") |             .withName("stringB") | ||||||
|             .withDescription("stringB") |             .withDescription("stringB") | ||||||
|             .withPropertyType(STRING_TYPE.getEntityReference()); |             .withPropertyType(STRING_TYPE.getEntityReference()); | ||||||
|     typeResourceTest.addCustomProperty(entity.getId(), fieldA, OK, ADMIN_AUTH_HEADERS); |     String json = JsonUtils.pojoToJson(entity); | ||||||
|     typeResourceTest.addCustomProperty(entity.getId(), fieldB, OK, ADMIN_AUTH_HEADERS); |  | ||||||
| 
 | 
 | ||||||
|     // Add invalid custom fields to the entity - custom field has invalid type |     ChangeDescription change = getChangeDescription(entity.getVersion()); | ||||||
|     Type INVALID_TYPE = |     change.getFieldsAdded().add(new FieldChange().withName("customProperties").withNewValue(Arrays.asList(fieldB))); | ||||||
|  |     entity.getCustomProperties().add(fieldB); | ||||||
|  |     entity = typeResourceTest.patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|  | 
 | ||||||
|  |     // PUT invalid custom fields to the entity - custom field has invalid type | ||||||
|  |     Type invalidType = | ||||||
|         new Type().withId(UUID.randomUUID()).withName("invalid").withCategory(Category.Field).withSchema("{}"); |         new Type().withId(UUID.randomUUID()).withName("invalid").withCategory(Category.Field).withSchema("{}"); | ||||||
|     CustomProperty fieldInvalid = |     CustomProperty fieldInvalid = | ||||||
|         new CustomProperty() |         new CustomProperty() | ||||||
|             .withName("invalid") |             .withName("invalid") | ||||||
|             .withDescription("invalid") |             .withDescription("invalid") | ||||||
|             .withPropertyType(INVALID_TYPE.getEntityReference()); |             .withPropertyType(invalidType.getEntityReference()); | ||||||
|     assertResponse( |     assertResponse( | ||||||
|         () -> typeResourceTest.addCustomProperty(entity.getId(), fieldInvalid, OK, ADMIN_AUTH_HEADERS), |         () -> typeResourceTest.addCustomProperty(id, fieldInvalid, OK, ADMIN_AUTH_HEADERS), | ||||||
|         NOT_FOUND, |         NOT_FOUND, | ||||||
|         CatalogExceptionMessage.entityNotFound(Entity.TYPE, INVALID_TYPE.getId())); |         CatalogExceptionMessage.entityNotFound(Entity.TYPE, invalidType.getId())); | ||||||
|  | 
 | ||||||
|  |     // PATCH invalid custom fields to the entity - custom field has invalid type | ||||||
|  |     String json1 = JsonUtils.pojoToJson(entity); | ||||||
|  |     entity.getCustomProperties().add(fieldInvalid); | ||||||
|  |     Type finalEntity = entity; | ||||||
|  |     assertResponse( | ||||||
|  |         () -> typeResourceTest.patchEntity(id, json1, finalEntity, ADMIN_AUTH_HEADERS), | ||||||
|  |         NOT_FOUND, | ||||||
|  |         CatalogExceptionMessage.entityNotFound(Entity.TYPE, invalidType.getId())); | ||||||
| 
 | 
 | ||||||
|     // Now create an entity with custom field |     // Now create an entity with custom field | ||||||
|     ObjectMapper mapper = new ObjectMapper(); |     ObjectMapper mapper = new ObjectMapper(); | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; | |||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.net.URISyntaxException; | import java.net.URISyntaxException; | ||||||
|  | import java.util.List; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| import java.util.UUID; | import java.util.UUID; | ||||||
| import javax.ws.rs.client.WebTarget; | import javax.ws.rs.client.WebTarget; | ||||||
| @ -42,6 +43,8 @@ import org.openmetadata.catalog.resources.EntityResourceTest; | |||||||
| import org.openmetadata.catalog.resources.types.TypeResource; | import org.openmetadata.catalog.resources.types.TypeResource; | ||||||
| import org.openmetadata.catalog.resources.types.TypeResource.TypeList; | import org.openmetadata.catalog.resources.types.TypeResource.TypeList; | ||||||
| import org.openmetadata.catalog.type.EntityReference; | import org.openmetadata.catalog.type.EntityReference; | ||||||
|  | import org.openmetadata.catalog.util.EntityUtil; | ||||||
|  | import org.openmetadata.catalog.util.JsonUtils; | ||||||
| import org.openmetadata.catalog.util.TestUtils; | import org.openmetadata.catalog.util.TestUtils; | ||||||
| 
 | 
 | ||||||
| @Slf4j | @Slf4j | ||||||
| @ -112,7 +115,7 @@ public class TypeResourceTest extends EntityResourceTest<Type, CreateType> { | |||||||
|     assertResponse( |     assertResponse( | ||||||
|         () -> addCustomProperty(INT_TYPE.getId(), field, Status.CREATED, ADMIN_AUTH_HEADERS), |         () -> addCustomProperty(INT_TYPE.getId(), field, Status.CREATED, ADMIN_AUTH_HEADERS), | ||||||
|         Status.BAD_REQUEST, |         Status.BAD_REQUEST, | ||||||
|         "Property types can't be extended"); |         "Only entity types can be extended and field types can't be extended"); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
| @ -125,6 +128,17 @@ public class TypeResourceTest extends EntityResourceTest<Type, CreateType> { | |||||||
|     return type; |     return type; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   public Type addAndCheckCustomProperty( | ||||||
|  |       UUID entityTypeId, CustomProperty customProperty, Status status, Map<String, String> authHeaders) | ||||||
|  |       throws HttpResponseException { | ||||||
|  |     Type updated = addCustomProperty(entityTypeId, customProperty, status, authHeaders); | ||||||
|  |     assertTrue(updated.getCustomProperties().contains(customProperty)); | ||||||
|  | 
 | ||||||
|  |     Type get = getEntity(entityTypeId, "customProperties", authHeaders); | ||||||
|  |     assertTrue(get.getCustomProperties().contains(customProperty)); | ||||||
|  |     return updated; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   public Type addCustomProperty( |   public Type addCustomProperty( | ||||||
|       UUID entityTypeId, CustomProperty customProperty, Status status, Map<String, String> authHeaders) |       UUID entityTypeId, CustomProperty customProperty, Status status, Map<String, String> authHeaders) | ||||||
|       throws HttpResponseException { |       throws HttpResponseException { | ||||||
| @ -159,6 +173,15 @@ public class TypeResourceTest extends EntityResourceTest<Type, CreateType> { | |||||||
|     if (expected == actual) { |     if (expected == actual) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     assertCommonFieldChange(fieldName, expected, actual); |     if (fieldName.equals("customProperties")) { | ||||||
|  |       List<CustomProperty> expectedProperties = (List<CustomProperty>) expected; | ||||||
|  |       List<CustomProperty> actualProperties = JsonUtils.readObjects(actual.toString(), CustomProperty.class); | ||||||
|  |       assertEquals(expectedProperties.size(), actualProperties.size()); | ||||||
|  |       expectedProperties.sort(EntityUtil.compareCustomProperty); | ||||||
|  |       actualProperties.sort(EntityUtil.compareCustomProperty); | ||||||
|  |       assertEquals(expectedProperties, actualProperties); | ||||||
|  |     } else { | ||||||
|  |       assertCommonFieldChange(fieldName, expected, actual); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Suresh Srinivas
						Suresh Srinivas