mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-31 02:29:03 +00:00 
			
		
		
		
	* Fix #18332: Do not consolidate changes for Parent Entities * Fix #18332: Do not consolidate changes for Parent Entities * added no data placeholder when no tags there * fix failing playwright test due to consolidation changes and added the missing services * Fix tests * Fix parent entity change notifications * Fix tests * Fix parent entity change notifications * Fix tests * Fix tests --------- Co-authored-by: Ashish Gupta <ashish@getcollate.io>
This commit is contained in:
		
							parent
							
								
									2539ffa8de
								
							
						
					
					
						commit
						680ace99b3
					
				| @ -243,6 +243,7 @@ public final class Entity { | |||||||
|   public static final String DOCUMENT = "document"; |   public static final String DOCUMENT = "document"; | ||||||
|   // ServiceType - Service Entity name map |   // ServiceType - Service Entity name map | ||||||
|   static final Map<ServiceType, String> SERVICE_TYPE_ENTITY_MAP = new EnumMap<>(ServiceType.class); |   static final Map<ServiceType, String> SERVICE_TYPE_ENTITY_MAP = new EnumMap<>(ServiceType.class); | ||||||
|  |   public static final List<String> PARENT_ENTITY_TYPES = new ArrayList<>(); | ||||||
| 
 | 
 | ||||||
|   static { |   static { | ||||||
|     SERVICE_TYPE_ENTITY_MAP.put(ServiceType.DATABASE, DATABASE_SERVICE); |     SERVICE_TYPE_ENTITY_MAP.put(ServiceType.DATABASE, DATABASE_SERVICE); | ||||||
| @ -254,6 +255,25 @@ public final class Entity { | |||||||
|     SERVICE_TYPE_ENTITY_MAP.put(ServiceType.STORAGE, STORAGE_SERVICE); |     SERVICE_TYPE_ENTITY_MAP.put(ServiceType.STORAGE, STORAGE_SERVICE); | ||||||
|     SERVICE_TYPE_ENTITY_MAP.put(ServiceType.SEARCH, SEARCH_SERVICE); |     SERVICE_TYPE_ENTITY_MAP.put(ServiceType.SEARCH, SEARCH_SERVICE); | ||||||
|     SERVICE_TYPE_ENTITY_MAP.put(ServiceType.API, API_SERVICE); |     SERVICE_TYPE_ENTITY_MAP.put(ServiceType.API, API_SERVICE); | ||||||
|  |     PARENT_ENTITY_TYPES.addAll( | ||||||
|  |         listOf( | ||||||
|  |             DATABASE_SERVICE, | ||||||
|  |             DASHBOARD_SERVICE, | ||||||
|  |             MESSAGING_SERVICE, | ||||||
|  |             MLMODEL_SERVICE, | ||||||
|  |             PIPELINE_SERVICE, | ||||||
|  |             API_SERVICE, | ||||||
|  |             API_COLLCECTION, | ||||||
|  |             STORAGE_SERVICE, | ||||||
|  |             METADATA_SERVICE, | ||||||
|  |             SEARCH_SERVICE, | ||||||
|  |             DATABASE, | ||||||
|  |             DATABASE_SCHEMA, | ||||||
|  |             CLASSIFICATION, | ||||||
|  |             GLOSSARY, | ||||||
|  |             DOMAIN, | ||||||
|  |             TEST_SUITE, | ||||||
|  |             TEAM)); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private Entity() {} |   private Entity() {} | ||||||
|  | |||||||
| @ -39,6 +39,7 @@ public class APICollectionRepository extends EntityRepository<APICollection> { | |||||||
|         "", |         "", | ||||||
|         ""); |         ""); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|  | |||||||
| @ -16,5 +16,6 @@ public class APIServiceRepository extends ServiceEntityRepository<ApiService, Ap | |||||||
|         "", |         "", | ||||||
|         ServiceType.API); |         ServiceType.API); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -54,6 +54,7 @@ public class ClassificationRepository extends EntityRepository<Classification> { | |||||||
|     quoteFqn = true; |     quoteFqn = true; | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|     renameAllowed = true; |     renameAllowed = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|  | |||||||
| @ -33,5 +33,6 @@ public class DashboardServiceRepository | |||||||
|         "", |         "", | ||||||
|         ServiceType.DASHBOARD); |         ServiceType.DASHBOARD); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -69,6 +69,7 @@ public class DatabaseRepository extends EntityRepository<Database> { | |||||||
|         "", |         "", | ||||||
|         ""); |         ""); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|     fieldFetchers.put("name", this::fetchAndSetService); |     fieldFetchers.put("name", this::fetchAndSetService); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -74,6 +74,7 @@ public class DatabaseSchemaRepository extends EntityRepository<DatabaseSchema> { | |||||||
|         "", |         "", | ||||||
|         ""); |         ""); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|  | |||||||
| @ -60,6 +60,7 @@ public class DatabaseServiceRepository | |||||||
|         "", |         "", | ||||||
|         ServiceType.DATABASE); |         ServiceType.DATABASE); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|  | |||||||
| @ -46,6 +46,7 @@ public class DomainRepository extends EntityRepository<Domain> { | |||||||
|         UPDATE_FIELDS, |         UPDATE_FIELDS, | ||||||
|         UPDATE_FIELDS); |         UPDATE_FIELDS); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|  | |||||||
| @ -260,6 +260,7 @@ public abstract class EntityRepository<T extends EntityInterface> { | |||||||
|   @Getter protected final Fields putFields; |   @Getter protected final Fields putFields; | ||||||
| 
 | 
 | ||||||
|   protected boolean supportsSearch = false; |   protected boolean supportsSearch = false; | ||||||
|  |   @Getter protected boolean parent = false; | ||||||
|   protected final Map<String, BiConsumer<List<T>, Fields>> fieldFetchers = new HashMap<>(); |   protected final Map<String, BiConsumer<List<T>, Fields>> fieldFetchers = new HashMap<>(); | ||||||
| 
 | 
 | ||||||
|   protected EntityRepository( |   protected EntityRepository( | ||||||
| @ -3276,7 +3277,9 @@ public abstract class EntityRepository<T extends EntityInterface> { | |||||||
| 
 | 
 | ||||||
|     private boolean consolidateChanges(T original, T updated, Operation operation) { |     private boolean consolidateChanges(T original, T updated, Operation operation) { | ||||||
|       // If user is the same and the new update is with in the user session timeout |       // If user is the same and the new update is with in the user session timeout | ||||||
|       return original.getVersion() > 0.1 // First update on an entity that |       return !parent // Parent entity shouldn't consolidate changes, as we need ChangeDescription to | ||||||
|  |           // propagate to children | ||||||
|  |           && original.getVersion() > 0.1 // First update on an entity that | ||||||
|           && operation == Operation.PATCH |           && operation == Operation.PATCH | ||||||
|           && !Boolean.TRUE.equals(original.getDeleted()) // Entity is not soft deleted |           && !Boolean.TRUE.equals(original.getDeleted()) // Entity is not soft deleted | ||||||
|           && !operation.isDelete() // Operation must be an update |           && !operation.isDelete() // Operation must be an update | ||||||
| @ -3285,6 +3288,7 @@ public abstract class EntityRepository<T extends EntityInterface> { | |||||||
|               .equals(updated.getUpdatedBy()) // Must be updated by the same user |               .equals(updated.getUpdatedBy()) // Must be updated by the same user | ||||||
|           && updated.getUpdatedAt() - original.getUpdatedAt() |           && updated.getUpdatedAt() - original.getUpdatedAt() | ||||||
|               <= sessionTimeoutMillis; // With in session timeout |               <= sessionTimeoutMillis; // With in session timeout | ||||||
|  |       // changes to children | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private T getPreviousVersion(T original) { |     private T getPreviousVersion(T original) { | ||||||
|  | |||||||
| @ -89,6 +89,7 @@ public class GlossaryRepository extends EntityRepository<Glossary> { | |||||||
|     quoteFqn = true; |     quoteFqn = true; | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|     renameAllowed = true; |     renameAllowed = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|  | |||||||
| @ -34,5 +34,6 @@ public class MessagingServiceRepository | |||||||
|         UPDATE_FIELDS, |         UPDATE_FIELDS, | ||||||
|         ServiceType.MESSAGING); |         ServiceType.MESSAGING); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -19,5 +19,6 @@ public class MetadataServiceRepository | |||||||
|         UPDATE_FIELDS, |         UPDATE_FIELDS, | ||||||
|         ServiceType.METADATA); |         ServiceType.METADATA); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -34,5 +34,6 @@ public class MlModelServiceRepository | |||||||
|         UPDATE_FIELDS, |         UPDATE_FIELDS, | ||||||
|         ServiceType.ML_MODEL); |         ServiceType.ML_MODEL); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -33,5 +33,6 @@ public class PipelineServiceRepository | |||||||
|         "", |         "", | ||||||
|         ServiceType.PIPELINE); |         ServiceType.PIPELINE); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,5 +17,6 @@ public class SearchServiceRepository | |||||||
|         "", |         "", | ||||||
|         ServiceType.SEARCH); |         ServiceType.SEARCH); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,5 +17,6 @@ public class StorageServiceRepository | |||||||
|         "", |         "", | ||||||
|         ServiceType.STORAGE); |         ServiceType.STORAGE); | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -102,6 +102,7 @@ public class TeamRepository extends EntityRepository<Team> { | |||||||
|         TEAM_UPDATE_FIELDS); |         TEAM_UPDATE_FIELDS); | ||||||
|     this.quoteFqn = true; |     this.quoteFqn = true; | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|  | |||||||
| @ -104,6 +104,7 @@ public class TestSuiteRepository extends EntityRepository<TestSuite> { | |||||||
|         UPDATE_FIELDS); |         UPDATE_FIELDS); | ||||||
|     quoteFqn = false; |     quoteFqn = false; | ||||||
|     supportsSearch = true; |     supportsSearch = true; | ||||||
|  |     parent = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Override |   @Override | ||||||
|  | |||||||
| @ -449,7 +449,7 @@ public class SearchRepository { | |||||||
|     Map<String, Object> fieldData = new HashMap<>(); |     Map<String, Object> fieldData = new HashMap<>(); | ||||||
| 
 | 
 | ||||||
|     if (changeDescription != null) { |     if (changeDescription != null) { | ||||||
|       for (FieldChange field : changeDescription.getFieldsAdded()) { |       for (FieldChange field : changeDescription.getFieldsDeleted()) { | ||||||
|         if (inheritableFields.contains(field.getName())) { |         if (inheritableFields.contains(field.getName())) { | ||||||
|           try { |           try { | ||||||
|             if (field.getName().equals(FIELD_OWNERS)) { |             if (field.getName().equals(FIELD_OWNERS)) { | ||||||
| @ -458,24 +458,21 @@ public class SearchRepository { | |||||||
|               for (EntityReference inheritedOwner : inheritedOwners) { |               for (EntityReference inheritedOwner : inheritedOwners) { | ||||||
|                 inheritedOwner.setInherited(true); |                 inheritedOwner.setInherited(true); | ||||||
|               } |               } | ||||||
|               fieldData.put("updatedOwners", inheritedOwners); |               fieldData.put("deletedOwners", inheritedOwners); | ||||||
|               scriptTxt.append(ADD_OWNERS_SCRIPT); |               scriptTxt.append(REMOVE_OWNERS_SCRIPT); | ||||||
|             } else { |             } else { | ||||||
|               EntityReference entityReference = |               EntityReference entityReference = | ||||||
|                   JsonUtils.readValue(field.getNewValue().toString(), EntityReference.class); |                   JsonUtils.readValue(field.getOldValue().toString(), EntityReference.class); | ||||||
|               scriptTxt.append( |               scriptTxt.append( | ||||||
|                   String.format( |                   String.format( | ||||||
|                       PROPAGATE_ENTITY_REFERENCE_FIELD_SCRIPT, |                       REMOVE_PROPAGATED_ENTITY_REFERENCE_FIELD_SCRIPT, | ||||||
|                       field.getName(), |  | ||||||
|                       field.getName(), |  | ||||||
|                       field.getName(), |                       field.getName(), | ||||||
|                       field.getName(), |                       field.getName(), | ||||||
|                       field.getName())); |                       field.getName())); | ||||||
|               fieldData.put(field.getName(), entityReference); |               fieldData.put(field.getName(), JsonUtils.getMap(entityReference)); | ||||||
|             } |             } | ||||||
|           } catch (UnhandledServerException e) { |           } catch (UnhandledServerException e) { | ||||||
|             scriptTxt.append( |             scriptTxt.append(String.format(REMOVE_PROPAGATED_FIELD_SCRIPT, field.getName())); | ||||||
|                 String.format(PROPAGATE_FIELD_SCRIPT, field.getName(), field.getNewValue())); |  | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
| @ -507,7 +504,7 @@ public class SearchRepository { | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|       for (FieldChange field : changeDescription.getFieldsDeleted()) { |       for (FieldChange field : changeDescription.getFieldsAdded()) { | ||||||
|         if (inheritableFields.contains(field.getName())) { |         if (inheritableFields.contains(field.getName())) { | ||||||
|           try { |           try { | ||||||
|             if (field.getName().equals(FIELD_OWNERS)) { |             if (field.getName().equals(FIELD_OWNERS)) { | ||||||
| @ -516,21 +513,24 @@ public class SearchRepository { | |||||||
|               for (EntityReference inheritedOwner : inheritedOwners) { |               for (EntityReference inheritedOwner : inheritedOwners) { | ||||||
|                 inheritedOwner.setInherited(true); |                 inheritedOwner.setInherited(true); | ||||||
|               } |               } | ||||||
|               fieldData.put("deletedOwners", inheritedOwners); |               fieldData.put("updatedOwners", inheritedOwners); | ||||||
|               scriptTxt.append(REMOVE_OWNERS_SCRIPT); |               scriptTxt.append(ADD_OWNERS_SCRIPT); | ||||||
|             } else { |             } else { | ||||||
|               EntityReference entityReference = |               EntityReference entityReference = | ||||||
|                   JsonUtils.readValue(field.getOldValue().toString(), EntityReference.class); |                   JsonUtils.readValue(field.getNewValue().toString(), EntityReference.class); | ||||||
|               scriptTxt.append( |               scriptTxt.append( | ||||||
|                   String.format( |                   String.format( | ||||||
|                       REMOVE_PROPAGATED_ENTITY_REFERENCE_FIELD_SCRIPT, |                       PROPAGATE_ENTITY_REFERENCE_FIELD_SCRIPT, | ||||||
|  |                       field.getName(), | ||||||
|  |                       field.getName(), | ||||||
|                       field.getName(), |                       field.getName(), | ||||||
|                       field.getName(), |                       field.getName(), | ||||||
|                       field.getName())); |                       field.getName())); | ||||||
|               fieldData.put(field.getName(), JsonUtils.getMap(entityReference)); |               fieldData.put(field.getName(), entityReference); | ||||||
|             } |             } | ||||||
|           } catch (UnhandledServerException e) { |           } catch (UnhandledServerException e) { | ||||||
|             scriptTxt.append(String.format(REMOVE_PROPAGATED_FIELD_SCRIPT, field.getName())); |             scriptTxt.append( | ||||||
|  |                 String.format(PROPAGATE_FIELD_SCRIPT, field.getName(), field.getNewValue())); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  | |||||||
| @ -1549,13 +1549,24 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|     checkOwnerOwns(TEAM11_REF, entity.getId(), true); |     checkOwnerOwns(TEAM11_REF, entity.getId(), true); | ||||||
| 
 | 
 | ||||||
|     // V0-2 (consolidated) - Change owner from TEAM_OWNER1 to USER_OWNER1 using PATCH request |     // V0-2 (consolidated) - Change owner from TEAM_OWNER1 to USER_OWNER1 using PATCH request | ||||||
|  |     if (!isParent()) { | ||||||
|       json = JsonUtils.pojoToJson(entity); |       json = JsonUtils.pojoToJson(entity); | ||||||
|       entity.setOwners(List.of(USER1_REF)); |       entity.setOwners(List.of(USER1_REF)); | ||||||
|     change = getChangeDescription(entity, CHANGE_CONSOLIDATED); |       change = getChangeDescription(entity, getChangeType()); | ||||||
|       fieldAdded(change, FIELD_OWNERS, List.of(USER1_REF)); |       fieldAdded(change, FIELD_OWNERS, List.of(USER1_REF)); | ||||||
|     entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |       entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
|       checkOwnerOwns(USER1_REF, entity.getId(), true); |       checkOwnerOwns(USER1_REF, entity.getId(), true); | ||||||
|       checkOwnerOwns(TEAM11_REF, entity.getId(), false); |       checkOwnerOwns(TEAM11_REF, entity.getId(), false); | ||||||
|  |     } else { | ||||||
|  |       json = JsonUtils.pojoToJson(entity); | ||||||
|  |       entity.setOwners(List.of(USER1_REF)); | ||||||
|  |       change = getChangeDescription(entity, getChangeType()); | ||||||
|  |       fieldAdded(change, FIELD_OWNERS, List.of(USER1_REF)); | ||||||
|  |       fieldDeleted(change, FIELD_OWNERS, List.of(TEAM11_REF)); | ||||||
|  |       entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
|  |       checkOwnerOwns(USER1_REF, entity.getId(), true); | ||||||
|  |       checkOwnerOwns(TEAM11_REF, entity.getId(), false); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // V0.2 (no change) - Set the owner to the existing owner. No ownership change must be recorded. |     // V0.2 (no change) - Set the owner to the existing owner. No ownership change must be recorded. | ||||||
|     json = JsonUtils.pojoToJson(entity); |     json = JsonUtils.pojoToJson(entity); | ||||||
| @ -1565,11 +1576,20 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
| 
 | 
 | ||||||
|     // V0.1 (revert) - Remove ownership (USER_OWNER1) using PATCH. We are back to original state no |     // V0.1 (revert) - Remove ownership (USER_OWNER1) using PATCH. We are back to original state no | ||||||
|     // owner and no change |     // owner and no change | ||||||
|  |     if (!isParent()) { | ||||||
|       json = JsonUtils.pojoToJson(entity); |       json = JsonUtils.pojoToJson(entity); | ||||||
|       entity.setOwners(null); |       entity.setOwners(null); | ||||||
|       change = getChangeDescription(entity, REVERT); |       change = getChangeDescription(entity, REVERT); | ||||||
|       patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, REVERT, change); |       patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, REVERT, change); | ||||||
|       checkOwnerOwns(USER1_REF, entity.getId(), false); |       checkOwnerOwns(USER1_REF, entity.getId(), false); | ||||||
|  |     } else { | ||||||
|  |       json = JsonUtils.pojoToJson(entity); | ||||||
|  |       entity.setOwners(null); | ||||||
|  |       change = getChangeDescription(entity, MINOR_UPDATE); | ||||||
|  |       fieldDeleted(change, FIELD_OWNERS, List.of(USER1_REF)); | ||||||
|  |       patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|  |       checkOwnerOwns(USER1_REF, entity.getId(), false); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // set random type as entity. Check if the ownership validate. |     // set random type as entity. Check if the ownership validate. | ||||||
|     T newEntity = entity; |     T newEntity = entity; | ||||||
| @ -1873,6 +1893,7 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|     // |     // | ||||||
|     // Replace description, add tags tier, owner - Changes are consolidated |     // Replace description, add tags tier, owner - Changes are consolidated | ||||||
|     // |     // | ||||||
|  |     if (!isParent()) { | ||||||
|       origJson = JsonUtils.pojoToJson(entity); |       origJson = JsonUtils.pojoToJson(entity); | ||||||
|       entity.setDescription("description1"); |       entity.setDescription("description1"); | ||||||
|       entity.setDisplayName("displayName1"); |       entity.setDisplayName("displayName1"); | ||||||
| @ -1891,9 +1912,7 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|             FIELD_TAGS, |             FIELD_TAGS, | ||||||
|             List.of(USER_ADDRESS_TAG_LABEL, GLOSSARY2_TERM1_LABEL, TIER1_TAG_LABEL)); |             List.of(USER_ADDRESS_TAG_LABEL, GLOSSARY2_TERM1_LABEL, TIER1_TAG_LABEL)); | ||||||
|       } |       } | ||||||
| 
 |       entity = patchEntityAndCheck(entity, origJson, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
|     entity = patchEntityAndCheck(entity, origJson, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |  | ||||||
| 
 |  | ||||||
|       // |       // | ||||||
|       // Remove description, tier, owner - Changes are reverted going to 0.1 version of the entity |       // Remove description, tier, owner - Changes are reverted going to 0.1 version of the entity | ||||||
|       // |       // | ||||||
| @ -1904,6 +1923,41 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|       entity.setOwners(null); |       entity.setOwners(null); | ||||||
|       entity.setTags(null); |       entity.setTags(null); | ||||||
|       patchEntityAndCheck(entity, origJson, ADMIN_AUTH_HEADERS, REVERT, change); |       patchEntityAndCheck(entity, origJson, ADMIN_AUTH_HEADERS, REVERT, change); | ||||||
|  |     } else { | ||||||
|  |       origJson = JsonUtils.pojoToJson(entity); | ||||||
|  |       entity.setDescription("description1"); | ||||||
|  |       entity.setDisplayName("displayName1"); | ||||||
|  |       change = getChangeDescription(entity, MINOR_UPDATE); | ||||||
|  |       fieldUpdated(change, "description", "description", "description1"); | ||||||
|  |       fieldUpdated(change, "displayName", "displayName", "displayName1"); | ||||||
|  |       if (supportsOwners) { | ||||||
|  |         entity.setOwners(List.of(USER1_REF)); | ||||||
|  |         fieldAdded(change, FIELD_OWNERS, List.of(USER1_REF)); | ||||||
|  |         fieldDeleted(change, FIELD_OWNERS, List.of(TEAM11_REF)); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (supportsTags) { | ||||||
|  |         entity.getTags().add(TIER1_TAG_LABEL); | ||||||
|  |         fieldAdded(change, FIELD_TAGS, List.of(TIER1_TAG_LABEL)); | ||||||
|  |       } | ||||||
|  |       entity = patchEntityAndCheck(entity, origJson, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
|  | 
 | ||||||
|  |       origJson = JsonUtils.pojoToJson(entity); | ||||||
|  |       change = getChangeDescription(entity, MINOR_UPDATE); | ||||||
|  |       entity.setDescription(""); | ||||||
|  |       entity.setDisplayName(null); | ||||||
|  |       entity.setOwners(null); | ||||||
|  |       entity.setTags(null); | ||||||
|  |       fieldUpdated(change, "description", "description1", ""); | ||||||
|  |       fieldDeleted(change, "displayName", "displayName1"); | ||||||
|  |       if (supportsOwners) { | ||||||
|  |         fieldDeleted(change, FIELD_OWNERS, List.of(USER1_REF)); | ||||||
|  |       } | ||||||
|  |       if (supportsTags) { | ||||||
|  |         fieldDeleted(change, FIELD_TAGS, List.of(TIER1_TAG_LABEL)); | ||||||
|  |       } | ||||||
|  |       patchEntityAndCheck(entity, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
| @ -1942,11 +1996,12 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
| 
 | 
 | ||||||
|     // Update description with a new description and the version changes as admin - the changes are |     // Update description with a new description and the version changes as admin - the changes are | ||||||
|     // consolidated |     // consolidated | ||||||
|  |     if (!isParent()) { | ||||||
|       json = JsonUtils.pojoToJson(entity); |       json = JsonUtils.pojoToJson(entity); | ||||||
|       entity.setDescription("description2"); |       entity.setDescription("description2"); | ||||||
|     change = getChangeDescription(entity, CHANGE_CONSOLIDATED); // New version remains the same |       change = getChangeDescription(entity, getChangeType()); // Version changes | ||||||
|       fieldUpdated(change, "description", "description", "description2"); |       fieldUpdated(change, "description", "description", "description2"); | ||||||
|     entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |       entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
| 
 | 
 | ||||||
|       // Update displayName with a new displayName - but as USER1 |       // Update displayName with a new displayName - but as USER1 | ||||||
|       // Since the previous change is done by a different user, changes ** are not ** consolidated |       // Since the previous change is done by a different user, changes ** are not ** consolidated | ||||||
| @ -1968,6 +2023,34 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|       fieldUpdated(change, "displayName", "displayName", "displayName1"); |       fieldUpdated(change, "displayName", "displayName", "displayName1"); | ||||||
|       patchEntityAndCheck(entity, json, authHeaders(USER1.getName()), MINOR_UPDATE, change); |       patchEntityAndCheck(entity, json, authHeaders(USER1.getName()), MINOR_UPDATE, change); | ||||||
|       EntityUpdater.setSessionTimeout(10 * 60 * 10000); // Reset the session timeout back |       EntityUpdater.setSessionTimeout(10 * 60 * 10000); // Reset the session timeout back | ||||||
|  | 
 | ||||||
|  |     } else { | ||||||
|  |       json = JsonUtils.pojoToJson(entity); | ||||||
|  |       entity.setDescription("description2"); | ||||||
|  |       change = getChangeDescription(entity, getChangeType()); // Version changes | ||||||
|  |       fieldUpdated(change, "description", "description1", "description2"); | ||||||
|  |       entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
|  | 
 | ||||||
|  |       // Update displayName with a new displayName - but as USER1 | ||||||
|  |       // Since the previous change is done by a different user, changes ** are not ** consolidated | ||||||
|  |       json = JsonUtils.pojoToJson(entity); | ||||||
|  |       entity.setDisplayName("displayName"); | ||||||
|  |       change = getChangeDescription(entity, MINOR_UPDATE); // Version changes | ||||||
|  |       fieldAdded(change, "displayName", "displayName"); | ||||||
|  |       entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|  | 
 | ||||||
|  |       // Update displayName to a new displayName. | ||||||
|  |       // In this test, the user who previously made a change makes the change after session timeout. | ||||||
|  |       // The changes are not consolidated. | ||||||
|  |       EntityUpdater.setSessionTimeout(1); // Reduce the session timeout for this test | ||||||
|  |       java.lang.Thread.sleep(2); | ||||||
|  |       json = JsonUtils.pojoToJson(entity); | ||||||
|  |       entity.setDisplayName("displayName1"); | ||||||
|  |       change = getChangeDescription(entity, MINOR_UPDATE); // Version changes | ||||||
|  |       fieldUpdated(change, "displayName", "displayName", "displayName1"); | ||||||
|  |       patchEntityAndCheck(entity, json, authHeaders(USER1.getName()), MINOR_UPDATE, change); | ||||||
|  |       EntityUpdater.setSessionTimeout(10 * 60 * 10000); // Reset the session timeout back | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
| @ -2058,17 +2141,21 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|     JsonNode stringBValue = mapper.convertValue("stringB", JsonNode.class); |     JsonNode stringBValue = mapper.convertValue("stringB", JsonNode.class); | ||||||
|     jsonNode.set("stringB", stringBValue); |     jsonNode.set("stringB", stringBValue); | ||||||
|     entity.setExtension(jsonNode); |     entity.setExtension(jsonNode); | ||||||
|     change = |     change = getChangeDescription(entity, getChangeType()); // PATCH operation update is not | ||||||
|         getChangeDescription( |     if (!isParent()) { | ||||||
|             entity, CHANGE_CONSOLIDATED); // Patch operation update is consolidated in a session |  | ||||||
|       fieldUpdated( |       fieldUpdated( | ||||||
|           change, |           change, | ||||||
|           EntityUtil.getExtensionField("intA"), |           EntityUtil.getExtensionField("intA"), | ||||||
|           mapper.convertValue(1, JsonNode.class), |           mapper.convertValue(1, JsonNode.class), | ||||||
|           intAValue); |           intAValue); | ||||||
|       fieldAdded(change, "extension", List.of(JsonUtils.getObjectNode("stringB", stringBValue))); |       fieldAdded(change, "extension", List.of(JsonUtils.getObjectNode("stringB", stringBValue))); | ||||||
|     entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |       entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
|       assertEquals(JsonUtils.valueToTree(jsonNode), JsonUtils.valueToTree(entity.getExtension())); |       assertEquals(JsonUtils.valueToTree(jsonNode), JsonUtils.valueToTree(entity.getExtension())); | ||||||
|  |     } else { | ||||||
|  |       fieldAdded(change, "extension", List.of(JsonUtils.getObjectNode("stringB", stringBValue))); | ||||||
|  |       entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
|  |       assertEquals(JsonUtils.valueToTree(jsonNode), JsonUtils.valueToTree(entity.getExtension())); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // PUT and remove field intA from the entity extension - *** for BOT this should be ignored *** |     // PUT and remove field intA from the entity extension - *** for BOT this should be ignored *** | ||||||
|     JsonNode oldNode = JsonUtils.valueToTree(entity.getExtension()); |     JsonNode oldNode = JsonUtils.valueToTree(entity.getExtension()); | ||||||
| @ -2088,6 +2175,7 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|     assertEquals( |     assertEquals( | ||||||
|         JsonUtils.valueToTree(create.getExtension()), JsonUtils.valueToTree(entity.getExtension())); |         JsonUtils.valueToTree(create.getExtension()), JsonUtils.valueToTree(entity.getExtension())); | ||||||
| 
 | 
 | ||||||
|  |     if (!isParent()) { | ||||||
|       // PATCH and remove field stringB from the entity extension |       // PATCH and remove field stringB from the entity extension | ||||||
|       json = JsonUtils.pojoToJson(entity); |       json = JsonUtils.pojoToJson(entity); | ||||||
|       jsonNode.remove("stringB"); |       jsonNode.remove("stringB"); | ||||||
| @ -2101,8 +2189,19 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|           List.of( |           List.of( | ||||||
|               JsonUtils.getObjectNode("intA", intAValue), |               JsonUtils.getObjectNode("intA", intAValue), | ||||||
|               JsonUtils.getObjectNode("stringB", stringBValue))); |               JsonUtils.getObjectNode("stringB", stringBValue))); | ||||||
|     entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |       entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
|       assertEquals(JsonUtils.valueToTree(jsonNode), JsonUtils.valueToTree(entity.getExtension())); |       assertEquals(JsonUtils.valueToTree(jsonNode), JsonUtils.valueToTree(entity.getExtension())); | ||||||
|  |     } else { | ||||||
|  |       json = JsonUtils.pojoToJson(entity); | ||||||
|  |       jsonNode.remove("stringB"); | ||||||
|  |       entity.setExtension(jsonNode); | ||||||
|  |       change = | ||||||
|  |           getChangeDescription( | ||||||
|  |               entity, MINOR_UPDATE); // PATCH operation update is consolidated into a session | ||||||
|  |       fieldDeleted(change, "extension", List.of(JsonUtils.getObjectNode("stringB", stringBValue))); | ||||||
|  |       entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change); | ||||||
|  |       assertEquals(JsonUtils.valueToTree(jsonNode), JsonUtils.valueToTree(entity.getExtension())); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Now set the entity custom property to an invalid value |     // Now set the entity custom property to an invalid value | ||||||
|     jsonNode.set( |     jsonNode.set( | ||||||
| @ -4115,4 +4214,16 @@ public abstract class EntityResourceTest<T extends EntityInterface, K extends Cr | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   public UpdateType getChangeType() { | ||||||
|  |     if (isParent()) { | ||||||
|  |       return MINOR_UPDATE; | ||||||
|  |     } else { | ||||||
|  |       return CHANGE_CONSOLIDATED; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public boolean isParent() { | ||||||
|  |     return PARENT_ENTITY_TYPES.contains(entityType); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -10,9 +10,7 @@ import static org.openmetadata.service.util.EntityUtil.fieldAdded; | |||||||
| import static org.openmetadata.service.util.EntityUtil.fieldDeleted; | import static org.openmetadata.service.util.EntityUtil.fieldDeleted; | ||||||
| import static org.openmetadata.service.util.EntityUtil.fieldUpdated; | import static org.openmetadata.service.util.EntityUtil.fieldUpdated; | ||||||
| import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS; | import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS; | ||||||
| import static org.openmetadata.service.util.TestUtils.UpdateType.CHANGE_CONSOLIDATED; |  | ||||||
| import static org.openmetadata.service.util.TestUtils.UpdateType.MINOR_UPDATE; | import static org.openmetadata.service.util.TestUtils.UpdateType.MINOR_UPDATE; | ||||||
| import static org.openmetadata.service.util.TestUtils.UpdateType.REVERT; |  | ||||||
| import static org.openmetadata.service.util.TestUtils.assertEntityReferenceNames; | import static org.openmetadata.service.util.TestUtils.assertEntityReferenceNames; | ||||||
| import static org.openmetadata.service.util.TestUtils.assertListNotNull; | import static org.openmetadata.service.util.TestUtils.assertListNotNull; | ||||||
| import static org.openmetadata.service.util.TestUtils.assertListNull; | import static org.openmetadata.service.util.TestUtils.assertListNull; | ||||||
| @ -71,21 +69,17 @@ public class DomainResourceTest extends EntityResourceTest<Domain, CreateDomain> | |||||||
|     fieldDeleted(change, "experts", listOf(USER2.getEntityReference())); |     fieldDeleted(change, "experts", listOf(USER2.getEntityReference())); | ||||||
|     domain = updateAndCheckEntity(create, Status.OK, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); |     domain = updateAndCheckEntity(create, Status.OK, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
| 
 | 
 | ||||||
|     // Add User2 back as expert using PATCH |  | ||||||
|     // Version 0. 2 - Changes from this PATCH is consolidated with the previous change resulting in |  | ||||||
|     // no change |  | ||||||
|     String json = JsonUtils.pojoToJson(domain); |     String json = JsonUtils.pojoToJson(domain); | ||||||
|     domain.withExperts(List.of(USER1.getEntityReference(), USER2.getEntityReference())); |     domain.withExperts(List.of(USER1.getEntityReference(), USER2.getEntityReference())); | ||||||
|     change = getChangeDescription(domain, REVERT); |     change = getChangeDescription(domain, MINOR_UPDATE); | ||||||
|     domain = patchEntityAndCheck(domain, json, ADMIN_AUTH_HEADERS, REVERT, change); |     fieldAdded(change, "experts", listOf(USER2.getEntityReference())); | ||||||
|  |     domain = patchEntityAndCheck(domain, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
| 
 | 
 | ||||||
|     // Remove User2 as expert using PATCH |  | ||||||
|     // Version 0.1 - Changes from this PATCH is consolidated with the previous two changes resulting |  | ||||||
|     // in deletion of USER2 |  | ||||||
|     json = JsonUtils.pojoToJson(domain); |     json = JsonUtils.pojoToJson(domain); | ||||||
|     change = getChangeDescription(domain, REVERT); |     change = getChangeDescription(domain, MINOR_UPDATE); | ||||||
|  |     fieldDeleted(change, "experts", listOf(USER2.getEntityReference())); | ||||||
|     domain.withExperts(List.of(USER1.getEntityReference())); |     domain.withExperts(List.of(USER1.getEntityReference())); | ||||||
|     patchEntityAndCheck(domain, json, ADMIN_AUTH_HEADERS, REVERT, change); |     patchEntityAndCheck(domain, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
| @ -104,9 +98,9 @@ public class DomainResourceTest extends EntityResourceTest<Domain, CreateDomain> | |||||||
|     // Changes from this PATCH is consolidated with the previous changes |     // Changes from this PATCH is consolidated with the previous changes | ||||||
|     String json = JsonUtils.pojoToJson(domain); |     String json = JsonUtils.pojoToJson(domain); | ||||||
|     domain.withDomainType(DomainType.CONSUMER_ALIGNED); |     domain.withDomainType(DomainType.CONSUMER_ALIGNED); | ||||||
|     change = getChangeDescription(domain, CHANGE_CONSOLIDATED); |     change = getChangeDescription(domain, MINOR_UPDATE); | ||||||
|     fieldUpdated(change, "domainType", DomainType.AGGREGATE, DomainType.CONSUMER_ALIGNED); |     fieldUpdated(change, "domainType", DomainType.SOURCE_ALIGNED, DomainType.CONSUMER_ALIGNED); | ||||||
|     patchEntityAndCheck(domain, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |     patchEntityAndCheck(domain, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
|  | |||||||
| @ -37,11 +37,11 @@ import static org.openmetadata.schema.type.ProviderType.SYSTEM; | |||||||
| import static org.openmetadata.schema.type.TaskType.RequestDescription; | import static org.openmetadata.schema.type.TaskType.RequestDescription; | ||||||
| import static org.openmetadata.service.security.SecurityUtil.authHeaders; | import static org.openmetadata.service.security.SecurityUtil.authHeaders; | ||||||
| import static org.openmetadata.service.util.EntityUtil.fieldAdded; | import static org.openmetadata.service.util.EntityUtil.fieldAdded; | ||||||
|  | import static org.openmetadata.service.util.EntityUtil.fieldDeleted; | ||||||
| import static org.openmetadata.service.util.EntityUtil.fieldUpdated; | import static org.openmetadata.service.util.EntityUtil.fieldUpdated; | ||||||
| import static org.openmetadata.service.util.EntityUtil.getFqn; | import static org.openmetadata.service.util.EntityUtil.getFqn; | ||||||
| import static org.openmetadata.service.util.EntityUtil.toTagLabels; | import static org.openmetadata.service.util.EntityUtil.toTagLabels; | ||||||
| import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS; | import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS; | ||||||
| import static org.openmetadata.service.util.TestUtils.UpdateType.CHANGE_CONSOLIDATED; |  | ||||||
| import static org.openmetadata.service.util.TestUtils.UpdateType.MINOR_UPDATE; | import static org.openmetadata.service.util.TestUtils.UpdateType.MINOR_UPDATE; | ||||||
| import static org.openmetadata.service.util.TestUtils.assertListNull; | import static org.openmetadata.service.util.TestUtils.assertListNull; | ||||||
| import static org.openmetadata.service.util.TestUtils.assertResponse; | import static org.openmetadata.service.util.TestUtils.assertResponse; | ||||||
| @ -168,11 +168,9 @@ public class GlossaryResourceTest extends EntityResourceTest<Glossary, CreateGlo | |||||||
|     glossary.withReviewers(List.of(USER1_REF, USER2_REF)); |     glossary.withReviewers(List.of(USER1_REF, USER2_REF)); | ||||||
|     change = |     change = | ||||||
|         getChangeDescription( |         getChangeDescription( | ||||||
|             glossary, |             glossary, MINOR_UPDATE); // PATCH operation update is consolidated in a user session | ||||||
|             CHANGE_CONSOLIDATED); // PATCH operation update is consolidated in a user session |     fieldAdded(change, "reviewers", List.of(USER2_REF)); | ||||||
|     fieldAdded(change, "reviewers", List.of(USER1_REF, USER2_REF)); |     glossary = patchEntityAndCheck(glossary, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|     glossary = |  | ||||||
|         patchEntityAndCheck(glossary, origJson, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |  | ||||||
| 
 | 
 | ||||||
|     // Create a glossary term and assign USER2 as a reviewer |     // Create a glossary term and assign USER2 as a reviewer | ||||||
|     GlossaryTermResourceTest glossaryTermResourceTest = new GlossaryTermResourceTest(); |     GlossaryTermResourceTest glossaryTermResourceTest = new GlossaryTermResourceTest(); | ||||||
| @ -220,10 +218,9 @@ public class GlossaryResourceTest extends EntityResourceTest<Glossary, CreateGlo | |||||||
|     glossary.withReviewers(List.of(USER2_REF)); |     glossary.withReviewers(List.of(USER2_REF)); | ||||||
|     change = |     change = | ||||||
|         getChangeDescription( |         getChangeDescription( | ||||||
|             glossary, |             glossary, MINOR_UPDATE); // PATCH operation update is consolidated in a user session | ||||||
|             CHANGE_CONSOLIDATED); // PATCH operation update is consolidated in a user session |     fieldDeleted(change, "reviewers", List.of(USER1_REF)); | ||||||
|     fieldAdded(change, "reviewers", List.of(USER2_REF)); |     patchEntityAndCheck(glossary, origJson, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|     patchEntityAndCheck(glossary, origJson, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |  | ||||||
| 
 | 
 | ||||||
|     // Verify that USER1_REF is removed from the reviewers for the terms inside the glossary |     // Verify that USER1_REF is removed from the reviewers for the terms inside the glossary | ||||||
|     GLOSSARY_TERM1 = |     GLOSSARY_TERM1 = | ||||||
|  | |||||||
| @ -52,10 +52,8 @@ import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS; | |||||||
| import static org.openmetadata.service.util.TestUtils.TEST_AUTH_HEADERS; | import static org.openmetadata.service.util.TestUtils.TEST_AUTH_HEADERS; | ||||||
| import static org.openmetadata.service.util.TestUtils.TEST_USER_NAME; | import static org.openmetadata.service.util.TestUtils.TEST_USER_NAME; | ||||||
| import static org.openmetadata.service.util.TestUtils.USER_WITH_CREATE_HEADERS; | import static org.openmetadata.service.util.TestUtils.USER_WITH_CREATE_HEADERS; | ||||||
| import static org.openmetadata.service.util.TestUtils.UpdateType.CHANGE_CONSOLIDATED; |  | ||||||
| import static org.openmetadata.service.util.TestUtils.UpdateType.MINOR_UPDATE; | import static org.openmetadata.service.util.TestUtils.UpdateType.MINOR_UPDATE; | ||||||
| import static org.openmetadata.service.util.TestUtils.UpdateType.NO_CHANGE; | import static org.openmetadata.service.util.TestUtils.UpdateType.NO_CHANGE; | ||||||
| import static org.openmetadata.service.util.TestUtils.UpdateType.REVERT; |  | ||||||
| import static org.openmetadata.service.util.TestUtils.assertListNotNull; | import static org.openmetadata.service.util.TestUtils.assertListNotNull; | ||||||
| import static org.openmetadata.service.util.TestUtils.assertResponse; | import static org.openmetadata.service.util.TestUtils.assertResponse; | ||||||
| import static org.openmetadata.service.util.TestUtils.validateEntityReferences; | import static org.openmetadata.service.util.TestUtils.validateEntityReferences; | ||||||
| @ -600,11 +598,12 @@ public class TeamResourceTest extends EntityResourceTest<Team, CreateTeam> { | |||||||
|     bu2 = updateAndCheckEntity(create, OK, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); |     bu2 = updateAndCheckEntity(create, OK, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
| 
 | 
 | ||||||
|     // Change bu2 parent from Organization to bu1 using PATCH operation. |     // Change bu2 parent from Organization to bu1 using PATCH operation. | ||||||
|     // Change from this PATCH is combined with the previous PUT resulting in no change |  | ||||||
|     String json = JsonUtils.pojoToJson(bu2); |     String json = JsonUtils.pojoToJson(bu2); | ||||||
|     change = getChangeDescription(bu2, REVERT); |     change = getChangeDescription(bu2, MINOR_UPDATE); | ||||||
|     bu2.setParents(List.of(bu1.getEntityReference())); |     bu2.setParents(List.of(bu1.getEntityReference())); | ||||||
|     patchEntityAndCheck(bu2, json, ADMIN_AUTH_HEADERS, REVERT, change); |     fieldAdded(change, "parents", List.of(bu1.getEntityReference())); | ||||||
|  |     fieldDeleted(change, "parents", List.of(ORG_TEAM.getEntityReference())); | ||||||
|  |     patchEntityAndCheck(bu2, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
| @ -626,11 +625,11 @@ public class TeamResourceTest extends EntityResourceTest<Team, CreateTeam> { | |||||||
|     fieldUpdated(change, "isJoinable", false, true); |     fieldUpdated(change, "isJoinable", false, true); | ||||||
|     team = patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); |     team = patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
| 
 | 
 | ||||||
|     // set isJoinable to false - change from this PATCH and the previous are consolidated resulting |  | ||||||
|     // in no change |  | ||||||
|     json = JsonUtils.pojoToJson(team); |     json = JsonUtils.pojoToJson(team); | ||||||
|     team.setIsJoinable(false); |     team.setIsJoinable(false); | ||||||
|     patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, NO_CHANGE, null); |     change = getChangeDescription(team, MINOR_UPDATE); | ||||||
|  |     fieldUpdated(change, "isJoinable", true, false); | ||||||
|  |     patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
| @ -672,10 +671,9 @@ public class TeamResourceTest extends EntityResourceTest<Team, CreateTeam> { | |||||||
|     int removeDefaultRoleIndex = new Random().nextInt(roles.size()); |     int removeDefaultRoleIndex = new Random().nextInt(roles.size()); | ||||||
|     EntityReference deletedRole = team.getDefaultRoles().get(removeDefaultRoleIndex); |     EntityReference deletedRole = team.getDefaultRoles().get(removeDefaultRoleIndex); | ||||||
|     team.getDefaultRoles().remove(removeDefaultRoleIndex); |     team.getDefaultRoles().remove(removeDefaultRoleIndex); | ||||||
|     change = getChangeDescription(team, CHANGE_CONSOLIDATED); |     change = getChangeDescription(team, MINOR_UPDATE); | ||||||
|     fieldDeleted(change, "users", CommonUtil.listOf(deletedUser)); |  | ||||||
|     fieldDeleted(change, "defaultRoles", CommonUtil.listOf(deletedRole)); |     fieldDeleted(change, "defaultRoles", CommonUtil.listOf(deletedRole)); | ||||||
|     patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |     patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
| @ -725,8 +723,10 @@ public class TeamResourceTest extends EntityResourceTest<Team, CreateTeam> { | |||||||
|     // resulting in no change |     // resulting in no change | ||||||
|     json = JsonUtils.pojoToJson(team); |     json = JsonUtils.pojoToJson(team); | ||||||
|     team.withPolicies(null); |     team.withPolicies(null); | ||||||
|     change = getChangeDescription(team, REVERT); |     change = getChangeDescription(team, MINOR_UPDATE); | ||||||
|     patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, REVERT, change); |     fieldDeleted( | ||||||
|  |         change, "policies", List.of(POLICY1.getEntityReference(), POLICY2.getEntityReference())); | ||||||
|  |     patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
| @ -768,13 +768,11 @@ public class TeamResourceTest extends EntityResourceTest<Team, CreateTeam> { | |||||||
|     fieldUpdated(change, "profile", PROFILE, profile1); |     fieldUpdated(change, "profile", PROFILE, profile1); | ||||||
|     team = patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); |     team = patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
| 
 | 
 | ||||||
|     // Remove profile from the team - Change from this PATCH and previous are consolidated to no |  | ||||||
|     // change |  | ||||||
|     json = JsonUtils.pojoToJson(team); |     json = JsonUtils.pojoToJson(team); | ||||||
|     team.withProfile(null); |     team.withProfile(null); | ||||||
|     change = getChangeDescription(team, CHANGE_CONSOLIDATED); |     change = getChangeDescription(team, MINOR_UPDATE); | ||||||
|     fieldDeleted(change, "profile", PROFILE); |     fieldDeleted(change, "profile", profile1); | ||||||
|     patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); |     patchEntityAndCheck(team, json, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
|  | |||||||
| @ -89,7 +89,7 @@ test('Glossary', async ({ page }) => { | |||||||
| 
 | 
 | ||||||
|     await page.reload(); |     await page.reload(); | ||||||
|     const versionPageResponse = page.waitForResponse( |     const versionPageResponse = page.waitForResponse( | ||||||
|       `/api/v1/glossaries/${glossary.responseData.id}/versions/0.2` |       `/api/v1/glossaries/${glossary.responseData.id}/versions/0.3` | ||||||
|     ); |     ); | ||||||
|     await page.click('[data-testid="version-button"]'); |     await page.click('[data-testid="version-button"]'); | ||||||
|     await versionPageResponse; |     await versionPageResponse; | ||||||
|  | |||||||
| @ -173,7 +173,9 @@ test('Classification Page', async ({ page }) => { | |||||||
|       ) |       ) | ||||||
|     ).not.toBeVisible(); |     ).not.toBeVisible(); | ||||||
| 
 | 
 | ||||||
|     await expect(page.getByTestId('saveAssociatedTag')).not.toBeVisible(); |     await expect(page.getByText('No Tags are available')).toBeVisible(); | ||||||
|  | 
 | ||||||
|  |     await expect(page.getByTestId('saveAssociatedTag')).toBeDisabled(); | ||||||
| 
 | 
 | ||||||
|     // Re-enable the disabled Classification
 |     // Re-enable the disabled Classification
 | ||||||
|     await classification.visitPage(page); |     await classification.visitPage(page); | ||||||
| @ -207,6 +209,7 @@ test('Classification Page', async ({ page }) => { | |||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   await test.step('Create classification with validation checks', async () => { |   await test.step('Create classification with validation checks', async () => { | ||||||
|  |     await classification.visitPage(page); | ||||||
|     await page.click('[data-testid="add-classification"]'); |     await page.click('[data-testid="add-classification"]'); | ||||||
|     await page.waitForSelector('.ant-modal-content', { |     await page.waitForSelector('.ant-modal-content', { | ||||||
|       state: 'visible', |       state: 'visible', | ||||||
|  | |||||||
| @ -80,12 +80,12 @@ test('Classification version page', async ({ page }) => { | |||||||
|   await patchClassificationResponse; |   await patchClassificationResponse; | ||||||
| 
 | 
 | ||||||
|   // Verify disabled state
 |   // Verify disabled state
 | ||||||
|   await page.click('[data-testid="version-button"]:has-text("0.2")'); |   await page.click('[data-testid="version-button"]:has-text("0.3")'); | ||||||
| 
 | 
 | ||||||
|   await expect(page.locator('[data-testid="disabled"]')).toBeVisible(); |   await expect(page.locator('[data-testid="disabled"]')).toBeVisible(); | ||||||
| 
 | 
 | ||||||
|   // Toggle back to enabled
 |   // Toggle back to enabled
 | ||||||
|   await page.click('[data-testid="version-button"]:has-text("0.2")'); |   await page.click('[data-testid="version-button"]:has-text("0.3")'); | ||||||
|   await page.click('[data-testid="manage-button"]'); |   await page.click('[data-testid="manage-button"]'); | ||||||
|   const patchClassificationResponse2 = page.waitForResponse( |   const patchClassificationResponse2 = page.waitForResponse( | ||||||
|     `/api/v1/classifications/${classification.responseData?.id}` |     `/api/v1/classifications/${classification.responseData?.id}` | ||||||
| @ -94,7 +94,7 @@ test('Classification version page', async ({ page }) => { | |||||||
|   await patchClassificationResponse2; |   await patchClassificationResponse2; | ||||||
| 
 | 
 | ||||||
|   // Verify enabled state
 |   // Verify enabled state
 | ||||||
|   await page.click('[data-testid="version-button"]:has-text("0.2")'); |   await page.click('[data-testid="version-button"]:has-text("0.4")'); | ||||||
| 
 | 
 | ||||||
|   await expect( |   await expect( | ||||||
|     page.locator(`[data-testid="classification-${classification.data.name}"]`) |     page.locator(`[data-testid="classification-${classification.data.name}"]`) | ||||||
|  | |||||||
| @ -153,7 +153,7 @@ entities.forEach((EntityClass) => { | |||||||
|           type: 'Users', |           type: 'Users', | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         const versionDetailResponse = page.waitForResponse(`**/versions/0.2`); |         const versionDetailResponse = page.waitForResponse(`**/versions/0.3`); | ||||||
|         await page.locator('[data-testid="version-button"]').click(); |         await page.locator('[data-testid="version-button"]').click(); | ||||||
|         await versionDetailResponse; |         await versionDetailResponse; | ||||||
| 
 | 
 | ||||||
| @ -169,7 +169,7 @@ entities.forEach((EntityClass) => { | |||||||
| 
 | 
 | ||||||
|         await assignTier(page, 'Tier1', entity.endpoint); |         await assignTier(page, 'Tier1', entity.endpoint); | ||||||
| 
 | 
 | ||||||
|         const versionDetailResponse = page.waitForResponse(`**/versions/0.2`); |         const versionDetailResponse = page.waitForResponse(`**/versions/0.4`); | ||||||
|         await page.locator('[data-testid="version-button"]').click(); |         await page.locator('[data-testid="version-button"]').click(); | ||||||
|         await versionDetailResponse; |         await versionDetailResponse; | ||||||
| 
 | 
 | ||||||
| @ -210,7 +210,7 @@ entities.forEach((EntityClass) => { | |||||||
| 
 | 
 | ||||||
|           await expect(deletedBadge).toHaveText('Deleted'); |           await expect(deletedBadge).toHaveText('Deleted'); | ||||||
| 
 | 
 | ||||||
|           const versionDetailResponse = page.waitForResponse(`**/versions/0.3`); |           const versionDetailResponse = page.waitForResponse(`**/versions/0.5`); | ||||||
|           await page.locator('[data-testid="version-button"]').click(); |           await page.locator('[data-testid="version-button"]').click(); | ||||||
|           await versionDetailResponse; |           await versionDetailResponse; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -136,7 +136,7 @@ export const addTagToTableColumn = async ( | |||||||
| 
 | 
 | ||||||
|   await expect( |   await expect( | ||||||
|     page.locator( |     page.locator( | ||||||
|       `[data-testid="classification-tags-${columnNumber}"] [data-testid="tags-container"] [data-testid="icon"]` |       `[data-testid="classification-tags-${columnNumber}"] [data-testid="tags-container"] [data-testid="tag-${tagFqn}"]` | ||||||
|     ) |     ) | ||||||
|   ).toBeVisible(); |   ).toBeVisible(); | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ | |||||||
| import { CloseOutlined } from '@ant-design/icons'; | import { CloseOutlined } from '@ant-design/icons'; | ||||||
| import { | import { | ||||||
|   Button, |   Button, | ||||||
|  |   Empty, | ||||||
|   Form, |   Form, | ||||||
|   Select, |   Select, | ||||||
|   SelectProps, |   SelectProps, | ||||||
| @ -187,6 +188,7 @@ const AsyncSelectList: FC<AsyncSelectListProps & SelectProps> = ({ | |||||||
|           <Button |           <Button | ||||||
|             className="update-btn" |             className="update-btn" | ||||||
|             data-testid="saveAssociatedTag" |             data-testid="saveAssociatedTag" | ||||||
|  |             disabled={isEmpty(tagOptions)} | ||||||
|             htmlType="submit" |             htmlType="submit" | ||||||
|             loading={isSubmitLoading} |             loading={isSubmitLoading} | ||||||
|             size="small" |             size="small" | ||||||
| @ -302,7 +304,18 @@ const AsyncSelectList: FC<AsyncSelectListProps & SelectProps> = ({ | |||||||
|       dropdownRender={dropdownRender} |       dropdownRender={dropdownRender} | ||||||
|       filterOption={false} |       filterOption={false} | ||||||
|       mode={mode} |       mode={mode} | ||||||
|       notFoundContent={isLoading ? <Loader size="small" /> : null} |       notFoundContent={ | ||||||
|  |         isLoading ? ( | ||||||
|  |           <Loader size="small" /> | ||||||
|  |         ) : ( | ||||||
|  |           <Empty | ||||||
|  |             description={t('label.no-entity-available', { | ||||||
|  |               entity: t('label.tag-plural'), | ||||||
|  |             })} | ||||||
|  |             image={Empty.PRESENTED_IMAGE_SIMPLE} | ||||||
|  |           /> | ||||||
|  |         ) | ||||||
|  |       } | ||||||
|       optionLabelProp="label" |       optionLabelProp="label" | ||||||
|       style={{ width: '100%' }} |       style={{ width: '100%' }} | ||||||
|       tagRender={customTagRender} |       tagRender={customTagRender} | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "Keine Beschreibung", |     "no-description": "Keine Beschreibung", | ||||||
|     "no-diff-available": "Keine Unterschiede verfügbar", |     "no-diff-available": "Keine Unterschiede verfügbar", | ||||||
|     "no-entity": "Keine {{entity}}", |     "no-entity": "Keine {{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "Keine passenden Datenanlagen gefunden", |     "no-matching-data-asset": "Keine passenden Datenanlagen gefunden", | ||||||
|     "no-of-test": " Nr. der Tests", |     "no-of-test": " Nr. der Tests", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "No description", |     "no-description": "No description", | ||||||
|     "no-diff-available": "No diff available", |     "no-diff-available": "No diff available", | ||||||
|     "no-entity": "No {{entity}}", |     "no-entity": "No {{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "No matching data assets found", |     "no-matching-data-asset": "No matching data assets found", | ||||||
|     "no-of-test": " No. of Test", |     "no-of-test": " No. of Test", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "Sin descripción", |     "no-description": "Sin descripción", | ||||||
|     "no-diff-available": "Sin diferencia disponible", |     "no-diff-available": "Sin diferencia disponible", | ||||||
|     "no-entity": "No hay {{entity}}", |     "no-entity": "No hay {{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "No se encontraron activos de datos coincidentes", |     "no-matching-data-asset": "No se encontraron activos de datos coincidentes", | ||||||
|     "no-of-test": "No. de prueba", |     "no-of-test": "No. de prueba", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "Aucune description", |     "no-description": "Aucune description", | ||||||
|     "no-diff-available": "Aucune différence disponible", |     "no-diff-available": "Aucune différence disponible", | ||||||
|     "no-entity": "Pas de {{entity}}", |     "no-entity": "Pas de {{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "Aucun actif de données trouvé", |     "no-matching-data-asset": "Aucun actif de données trouvé", | ||||||
|     "no-of-test": " No. de Tests", |     "no-of-test": " No. de Tests", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "-", |     "no-description": "-", | ||||||
|     "no-diff-available": "אין הבדל זמין", |     "no-diff-available": "אין הבדל זמין", | ||||||
|     "no-entity": "אין {{entity}}", |     "no-entity": "אין {{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "לא נמצאו נכסי נתונים תואמים", |     "no-matching-data-asset": "לא נמצאו נכסי נתונים תואמים", | ||||||
|     "no-of-test": "מספר הבדיקות", |     "no-of-test": "מספר הבדיקות", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "説明がありません", |     "no-description": "説明がありません", | ||||||
|     "no-diff-available": "差分を見ることはできません", |     "no-diff-available": "差分を見ることはできません", | ||||||
|     "no-entity": "No {{entity}}", |     "no-entity": "No {{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "マッチするデータアセットはありません", |     "no-matching-data-asset": "マッチするデータアセットはありません", | ||||||
|     "no-of-test": " テスト番号", |     "no-of-test": " テスト番号", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "Geen beschrijving", |     "no-description": "Geen beschrijving", | ||||||
|     "no-diff-available": "Geen verschil beschikbaar", |     "no-diff-available": "Geen verschil beschikbaar", | ||||||
|     "no-entity": "Geen {{entity}}", |     "no-entity": "Geen {{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "Geen overeenkomende data-assets gevonden", |     "no-matching-data-asset": "Geen overeenkomende data-assets gevonden", | ||||||
|     "no-of-test": " Aantal tests", |     "no-of-test": " Aantal tests", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "بدون توضیح", |     "no-description": "بدون توضیح", | ||||||
|     "no-diff-available": "هیچ تفاوتی موجود نیست", |     "no-diff-available": "هیچ تفاوتی موجود نیست", | ||||||
|     "no-entity": "هیچ {{entity}}", |     "no-entity": "هیچ {{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "هیچ {{entity}} انتخاب نشده", |     "no-entity-selected": "هیچ {{entity}} انتخاب نشده", | ||||||
|     "no-matching-data-asset": "هیچ دارایی دادهی منطبق یافت نشد", |     "no-matching-data-asset": "هیچ دارایی دادهی منطبق یافت نشد", | ||||||
|     "no-of-test": "تعداد تست", |     "no-of-test": "تعداد تست", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "Sem descrição", |     "no-description": "Sem descrição", | ||||||
|     "no-diff-available": "Nenhuma diferença disponível", |     "no-diff-available": "Nenhuma diferença disponível", | ||||||
|     "no-entity": "Nenhum(a) {{entity}}", |     "no-entity": "Nenhum(a) {{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "Nenhum ativo de dados correspondente encontrado", |     "no-matching-data-asset": "Nenhum ativo de dados correspondente encontrado", | ||||||
|     "no-of-test": " Nº de Teste", |     "no-of-test": " Nº de Teste", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "Нет описания", |     "no-description": "Нет описания", | ||||||
|     "no-diff-available": "Нет различий", |     "no-diff-available": "Нет различий", | ||||||
|     "no-entity": "{{entity}} отсутствует", |     "no-entity": "{{entity}} отсутствует", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "Подходящие объекты данных не найдены", |     "no-matching-data-asset": "Подходящие объекты данных не найдены", | ||||||
|     "no-of-test": "№ теста", |     "no-of-test": "№ теста", | ||||||
|  | |||||||
| @ -773,6 +773,7 @@ | |||||||
|     "no-description": "无描述", |     "no-description": "无描述", | ||||||
|     "no-diff-available": "没有可用的差异", |     "no-diff-available": "没有可用的差异", | ||||||
|     "no-entity": "没有{{entity}}", |     "no-entity": "没有{{entity}}", | ||||||
|  |     "no-entity-available": "No {{entity}} are available", | ||||||
|     "no-entity-selected": "No {{entity}} Selected", |     "no-entity-selected": "No {{entity}} Selected", | ||||||
|     "no-matching-data-asset": "未找到匹配的数据资产", |     "no-matching-data-asset": "未找到匹配的数据资产", | ||||||
|     "no-of-test": "测试数量", |     "no-of-test": "测试数量", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sriharsha Chintalapani
						Sriharsha Chintalapani