Fix #18332: Do not consolidate changes for Parent Entities (#18333)

* 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:
Sriharsha Chintalapani 2024-10-31 11:48:46 -07:00 committed by GitHub
parent 2539ffa8de
commit 680ace99b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 331 additions and 163 deletions

View File

@ -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() {}

View File

@ -39,6 +39,7 @@ public class APICollectionRepository extends EntityRepository<APICollection> {
"", "",
""); "");
supportsSearch = true; supportsSearch = true;
parent = true;
} }
@Override @Override

View File

@ -16,5 +16,6 @@ public class APIServiceRepository extends ServiceEntityRepository<ApiService, Ap
"", "",
ServiceType.API); ServiceType.API);
supportsSearch = true; supportsSearch = true;
parent = true;
} }
} }

View File

@ -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

View File

@ -33,5 +33,6 @@ public class DashboardServiceRepository
"", "",
ServiceType.DASHBOARD); ServiceType.DASHBOARD);
supportsSearch = true; supportsSearch = true;
parent = true;
} }
} }

View File

@ -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);
} }

View File

@ -74,6 +74,7 @@ public class DatabaseSchemaRepository extends EntityRepository<DatabaseSchema> {
"", "",
""); "");
supportsSearch = true; supportsSearch = true;
parent = true;
} }
@Override @Override

View File

@ -60,6 +60,7 @@ public class DatabaseServiceRepository
"", "",
ServiceType.DATABASE); ServiceType.DATABASE);
supportsSearch = true; supportsSearch = true;
parent = true;
} }
@Override @Override

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -34,5 +34,6 @@ public class MessagingServiceRepository
UPDATE_FIELDS, UPDATE_FIELDS,
ServiceType.MESSAGING); ServiceType.MESSAGING);
supportsSearch = true; supportsSearch = true;
parent = true;
} }
} }

View File

@ -19,5 +19,6 @@ public class MetadataServiceRepository
UPDATE_FIELDS, UPDATE_FIELDS,
ServiceType.METADATA); ServiceType.METADATA);
supportsSearch = true; supportsSearch = true;
parent = true;
} }
} }

View File

@ -34,5 +34,6 @@ public class MlModelServiceRepository
UPDATE_FIELDS, UPDATE_FIELDS,
ServiceType.ML_MODEL); ServiceType.ML_MODEL);
supportsSearch = true; supportsSearch = true;
parent = true;
} }
} }

View File

@ -33,5 +33,6 @@ public class PipelineServiceRepository
"", "",
ServiceType.PIPELINE); ServiceType.PIPELINE);
supportsSearch = true; supportsSearch = true;
parent = true;
} }
} }

View File

@ -17,5 +17,6 @@ public class SearchServiceRepository
"", "",
ServiceType.SEARCH); ServiceType.SEARCH);
supportsSearch = true; supportsSearch = true;
parent = true;
} }
} }

View File

@ -17,5 +17,6 @@ public class StorageServiceRepository
"", "",
ServiceType.STORAGE); ServiceType.STORAGE);
supportsSearch = true; supportsSearch = true;
parent = true;
} }
} }

View File

@ -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

View File

@ -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

View File

@ -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()));
} }
} }
} }

View File

@ -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
json = JsonUtils.pojoToJson(entity); if (!isParent()) {
entity.setOwners(List.of(USER1_REF)); json = JsonUtils.pojoToJson(entity);
change = getChangeDescription(entity, CHANGE_CONSOLIDATED); entity.setOwners(List.of(USER1_REF));
fieldAdded(change, FIELD_OWNERS, List.of(USER1_REF)); change = getChangeDescription(entity, getChangeType());
entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); fieldAdded(change, FIELD_OWNERS, List.of(USER1_REF));
checkOwnerOwns(USER1_REF, entity.getId(), true); entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change);
checkOwnerOwns(TEAM11_REF, entity.getId(), false); checkOwnerOwns(USER1_REF, entity.getId(), true);
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
json = JsonUtils.pojoToJson(entity); if (!isParent()) {
entity.setOwners(null); json = JsonUtils.pojoToJson(entity);
change = getChangeDescription(entity, REVERT); entity.setOwners(null);
patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, REVERT, change); change = getChangeDescription(entity, REVERT);
checkOwnerOwns(USER1_REF, entity.getId(), false); patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, REVERT, change);
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,37 +1893,71 @@ 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
// //
origJson = JsonUtils.pojoToJson(entity); if (!isParent()) {
entity.setDescription("description1"); origJson = JsonUtils.pojoToJson(entity);
entity.setDisplayName("displayName1"); entity.setDescription("description1");
change = getChangeDescription(entity, CHANGE_CONSOLIDATED); entity.setDisplayName("displayName1");
fieldUpdated(change, "description", "", "description1"); change = getChangeDescription(entity, CHANGE_CONSOLIDATED);
fieldAdded(change, "displayName", "displayName1"); fieldUpdated(change, "description", "", "description1");
if (supportsOwners) { fieldAdded(change, "displayName", "displayName1");
entity.setOwners(List.of(USER1_REF)); if (supportsOwners) {
fieldAdded(change, FIELD_OWNERS, List.of(USER1_REF)); entity.setOwners(List.of(USER1_REF));
fieldAdded(change, FIELD_OWNERS, List.of(USER1_REF));
}
if (supportsTags) {
entity.getTags().add(TIER1_TAG_LABEL);
fieldAdded(
change,
FIELD_TAGS,
List.of(USER_ADDRESS_TAG_LABEL, GLOSSARY2_TERM1_LABEL, TIER1_TAG_LABEL));
}
entity = patchEntityAndCheck(entity, origJson, ADMIN_AUTH_HEADERS, getChangeType(), change);
//
// Remove description, tier, owner - Changes are reverted going to 0.1 version of the entity
//
origJson = JsonUtils.pojoToJson(entity);
change = getChangeDescription(entity, REVERT);
entity.setDescription("");
entity.setDisplayName(null);
entity.setOwners(null);
entity.setTags(null);
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);
} }
if (supportsTags) {
entity.getTags().add(TIER1_TAG_LABEL);
fieldAdded(
change,
FIELD_TAGS,
List.of(USER_ADDRESS_TAG_LABEL, GLOSSARY2_TERM1_LABEL, TIER1_TAG_LABEL));
}
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
//
origJson = JsonUtils.pojoToJson(entity);
change = getChangeDescription(entity, REVERT);
entity.setDescription("");
entity.setDisplayName(null);
entity.setOwners(null);
entity.setTags(null);
patchEntityAndCheck(entity, origJson, ADMIN_AUTH_HEADERS, REVERT, change);
} }
@Test @Test
@ -1942,32 +1996,61 @@ 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
json = JsonUtils.pojoToJson(entity); if (!isParent()) {
entity.setDescription("description2"); json = JsonUtils.pojoToJson(entity);
change = getChangeDescription(entity, CHANGE_CONSOLIDATED); // New version remains the same entity.setDescription("description2");
fieldUpdated(change, "description", "description", "description2"); change = getChangeDescription(entity, getChangeType()); // Version changes
entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); fieldUpdated(change, "description", "description", "description2");
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
json = JsonUtils.pojoToJson(entity); json = JsonUtils.pojoToJson(entity);
entity.setDisplayName("displayName"); entity.setDisplayName("displayName");
change = getChangeDescription(entity, CHANGE_CONSOLIDATED); // Version changes change = getChangeDescription(entity, CHANGE_CONSOLIDATED); // Version changes
fieldUpdated(change, "description", "description", "description2"); fieldUpdated(change, "description", "description", "description2");
fieldAdded(change, "displayName", "displayName"); fieldAdded(change, "displayName", "displayName");
entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change);
// Update displayName to a new displayName. // Update displayName to a new displayName.
// In this test, the user who previously made a change makes the change after session timeout. // In this test, the user who previously made a change makes the change after session timeout.
// The changes are not consolidated. // The changes are not consolidated.
EntityUpdater.setSessionTimeout(1); // Reduce the session timeout for this test EntityUpdater.setSessionTimeout(1); // Reduce the session timeout for this test
java.lang.Thread.sleep(2); java.lang.Thread.sleep(2);
json = JsonUtils.pojoToJson(entity); json = JsonUtils.pojoToJson(entity);
entity.setDisplayName("displayName1"); entity.setDisplayName("displayName1");
change = getChangeDescription(entity, MINOR_UPDATE); // Version changes change = getChangeDescription(entity, MINOR_UPDATE); // Version changes
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, getChangeType(), change);
entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, 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,21 +2175,33 @@ 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()));
// PATCH and remove field stringB from the entity extension if (!isParent()) {
json = JsonUtils.pojoToJson(entity); // PATCH and remove field stringB from the entity extension
jsonNode.remove("stringB"); json = JsonUtils.pojoToJson(entity);
entity.setExtension(jsonNode); jsonNode.remove("stringB");
change = entity.setExtension(jsonNode);
getChangeDescription( change =
entity, CHANGE_CONSOLIDATED); // PATCH operation update is consolidated into a session getChangeDescription(
fieldDeleted( entity, CHANGE_CONSOLIDATED); // PATCH operation update is consolidated into a session
change, fieldDeleted(
"extension", change,
List.of( "extension",
JsonUtils.getObjectNode("intA", intAValue), List.of(
JsonUtils.getObjectNode("stringB", stringBValue))); JsonUtils.getObjectNode("intA", intAValue),
entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); JsonUtils.getObjectNode("stringB", stringBValue)));
assertEquals(JsonUtils.valueToTree(jsonNode), JsonUtils.valueToTree(entity.getExtension())); entity = patchEntityAndCheck(entity, json, ADMIN_AUTH_HEADERS, getChangeType(), change);
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);
}
} }

View File

@ -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

View File

@ -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 =

View File

@ -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

View File

@ -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;

View File

@ -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',

View File

@ -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}"]`)

View File

@ -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;

View File

@ -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();
}; };

View File

@ -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}

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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": "מספר הבדיקות",

View File

@ -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": " テスト番号",

View File

@ -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",

View File

@ -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": "تعداد تست",

View File

@ -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",

View File

@ -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": "№ теста",

View File

@ -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": "测试数量",