Fixes #3069 - Add PATCH support Glossary and GlossaryTerms (#3070)

This commit is contained in:
Suresh Srinivas 2022-03-02 01:02:16 -08:00 committed by GitHub
parent bf25787709
commit c56e78f3f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 272 additions and 83 deletions

View File

@ -408,6 +408,7 @@ public abstract class EntityRepository<T> {
T original = setFields(dao.findEntityById(id), patchFields);
// Apply JSON patch to the original entity to get the updated entity
System.out.println("XXX patch is " + patch);
T updated = JsonUtils.applyPatch(original, patch, entityClass);
EntityInterface<T> updatedEntity = getEntityInterface(updated);
updatedEntity.setUpdateDetails(user, System.currentTimeMillis());
@ -861,6 +862,14 @@ public abstract class EntityRepository<T> {
return daoCollection.relationshipDAO().insert(fromId, toId, fromEntity, toEntity, relationship.ordinal());
}
public int addBidirectionalRelationship(
UUID fromId, UUID toId, String fromEntity, String toEntity, Relationship relationship) {
if (fromId.compareTo(toId) < 0) {
return daoCollection.relationshipDAO().insert(fromId, toId, fromEntity, toEntity, relationship.ordinal());
}
return daoCollection.relationshipDAO().insert(toId, fromId, toEntity, fromEntity, relationship.ordinal());
}
public void setOwner(UUID ownedEntityId, String ownedEntityType, EntityReference owner) {
// Add relationship owner --- owns ---> ownedEntity
if (owner != null) {
@ -891,6 +900,26 @@ public abstract class EntityRepository<T> {
.findTo(fromId.toString(), fromEntity, relationship.ordinal(), toEntity, deleted);
}
public void validateUsers(List<EntityReference> entityReferences) throws IOException {
if (entityReferences != null) {
entityReferences.sort(EntityUtil.compareEntityReference);
for (EntityReference entityReference : entityReferences) {
EntityReference ref = daoCollection.userDAO().findEntityReferenceById(entityReference.getId());
entityReference.withType(ref.getType()).withName(ref.getName()).withDisplayName(ref.getDisplayName());
}
}
}
public void validateRoles(List<EntityReference> entityReferences) throws IOException {
if (entityReferences != null) {
entityReferences.sort(EntityUtil.compareEntityReference);
for (EntityReference entityReference : entityReferences) {
EntityReference ref = daoCollection.roleDAO().findEntityReferenceById(entityReference.getId());
entityReference.withType(ref.getType()).withName(ref.getName()).withDisplayName(ref.getDisplayName());
}
}
}
enum Operation {
PUT,
PATCH,
@ -1135,6 +1164,77 @@ public abstract class EntityRepository<T> {
return !addedItems.isEmpty() || !deletedItems.isEmpty();
}
/**
* Remove `fromEntityType:fromId` -- `relationType` ---> `toEntityType:origToRefs` Add `fromEntityType:fromId` --
* `relationType` ---> `toEntityType:updatedToRefs` and record it as change for entity field `field`.
*/
public final void updateToRelationships(
String field,
String fromEntityType,
UUID fromId,
Relationship relationshipType,
String toEntityType,
List<EntityReference> origToRefs,
List<EntityReference> updatedToRefs)
throws JsonProcessingException {
List<EntityReference> added = new ArrayList<>();
List<EntityReference> deleted = new ArrayList<>();
if (!recordListChange(field, origToRefs, updatedToRefs, added, deleted, entityReferenceMatch)) {
// No changes between original and updated.
return;
}
// Remove relationships from original
daoCollection
.relationshipDAO()
.deleteFrom(fromId.toString(), fromEntityType, relationshipType.ordinal(), toEntityType);
// Add relationships from updated
for (EntityReference ref : updatedToRefs) {
System.out.println(
String.format(
"XXX relationship %s:%s to %s:%s of type %s",
fromEntityType, fromId, toEntityType, ref.getId(), relationshipType));
addRelationship(fromId, ref.getId(), fromEntityType, toEntityType, relationshipType);
}
updatedToRefs.sort(EntityUtil.compareEntityReference);
origToRefs.sort(EntityUtil.compareEntityReference);
}
/**
* Remove `fromEntityType:origFromRefs` -- `relationType` ---> `toEntityType:toId` Add
* `fromEntityType:updatedFromRefs` -- `relationType` ---> `toEntityType:toId` and record it as change for entity
* field `field`.
*/
public final void updateFromRelationships(
String field,
String fromEntityType,
List<EntityReference> originFromRefs,
List<EntityReference> updatedFromRefs,
Relationship relationshipType,
String toEntityType,
UUID toId)
throws JsonProcessingException {
List<EntityReference> added = new ArrayList<>();
List<EntityReference> deleted = new ArrayList<>();
if (!recordListChange(field, originFromRefs, updatedFromRefs, added, deleted, entityReferenceMatch)) {
// No changes between original and updated.
return;
}
// Remove relationships from original
daoCollection
.relationshipDAO()
.deleteTo(toId.toString(), fromEntityType, relationshipType.ordinal(), toEntityType);
// Add relationships from updated
for (EntityReference ref : updatedFromRefs) {
System.out.println(
String.format(
"XXX relationship %s:%s to %s:%s of type %s",
fromEntityType, ref, toEntityType, toId, relationshipType));
addRelationship(ref.getId(), toId, fromEntityType, toEntityType, relationshipType);
}
updatedFromRefs.sort(EntityUtil.compareEntityReference);
originFromRefs.sort(EntityUtil.compareEntityReference);
}
public final void storeUpdate() throws IOException {
if (updateVersion(original.getVersion())) { // Update changed the entity version
storeOldVersion(); // Store old version for listing previous versions of the entity

View File

@ -19,10 +19,13 @@ package org.openmetadata.catalog.jdbi3;
import static org.openmetadata.catalog.Entity.FIELD_OWNER;
import static org.openmetadata.catalog.Entity.helper;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import java.net.URI;
import java.text.ParseException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.catalog.Entity;
@ -30,6 +33,7 @@ import org.openmetadata.catalog.entity.data.Glossary;
import org.openmetadata.catalog.resources.glossary.GlossaryResource;
import org.openmetadata.catalog.type.ChangeDescription;
import org.openmetadata.catalog.type.EntityReference;
import org.openmetadata.catalog.type.Relationship;
import org.openmetadata.catalog.type.TagLabel;
import org.openmetadata.catalog.util.EntityInterface;
import org.openmetadata.catalog.util.EntityUtil;
@ -37,8 +41,8 @@ import org.openmetadata.catalog.util.EntityUtil.Fields;
import org.openmetadata.catalog.util.JsonUtils;
public class GlossaryRepository extends EntityRepository<Glossary> {
private static final Fields GLOSSARY_UPDATE_FIELDS = new Fields(GlossaryResource.ALLOWED_FIELDS, "owner,tags");
private static final Fields GLOSSARY_PATCH_FIELDS = new Fields(GlossaryResource.ALLOWED_FIELDS, "owner,tags");
private static final Fields UPDATE_FIELDS = new Fields(GlossaryResource.ALLOWED_FIELDS, "owner,tags,reviewers");
private static final Fields PATCH_FIELDS = new Fields(GlossaryResource.ALLOWED_FIELDS, "owner,tags,reviewers");
public GlossaryRepository(CollectionDAO dao) {
super(
@ -47,8 +51,8 @@ public class GlossaryRepository extends EntityRepository<Glossary> {
Glossary.class,
dao.glossaryDAO(),
dao,
GLOSSARY_PATCH_FIELDS,
GLOSSARY_UPDATE_FIELDS,
PATCH_FIELDS,
UPDATE_FIELDS,
true,
true,
false);
@ -63,13 +67,14 @@ public class GlossaryRepository extends EntityRepository<Glossary> {
public Glossary setFields(Glossary glossary, Fields fields) throws IOException, ParseException {
glossary.setOwner(fields.contains(FIELD_OWNER) ? getOwner(glossary) : null);
glossary.setTags(fields.contains("tags") ? getTags(glossary.getName()) : null);
glossary.setReviewers(fields.contains("reviewers") ? getReviewers(glossary) : null);
return glossary;
}
@Override
public void prepare(Glossary glossary) throws IOException, ParseException {
glossary.setOwner(helper(glossary).validateOwnerOrNull());
// TODO validate reviewers
validateUsers(glossary.getReviewers());
glossary.setTags(EntityUtil.addDerivedTags(daoCollection.tagDAO(), glossary.getTags()));
}
@ -78,7 +83,7 @@ public class GlossaryRepository extends EntityRepository<Glossary> {
// Relationships and fields such as href are derived and not stored as part of json
EntityReference owner = glossary.getOwner();
List<TagLabel> tags = glossary.getTags();
// TODO Add relationships for reviewers
List<EntityReference> reviewers = glossary.getReviewers();
// Don't store owner, href and tags as JSON. Build it on the fly based on relationships
glossary.withOwner(null).withHref(null).withTags(null);
@ -90,14 +95,16 @@ public class GlossaryRepository extends EntityRepository<Glossary> {
}
// Restore the relationships
glossary.withOwner(owner).withTags(tags);
glossary.withOwner(owner).withTags(tags).withReviewers(reviewers);
}
@Override
public void storeRelationships(Glossary glossary) {
// TODO Add relationships for related terms, and reviewers
setOwner(glossary, glossary.getOwner());
applyTags(glossary);
for (EntityReference reviewer : Optional.ofNullable(glossary.getReviewers()).orElse(Collections.emptyList())) {
addRelationship(reviewer.getId(), glossary.getId(), Entity.USER, Entity.GLOSSARY, Relationship.REVIEWS);
}
}
@Override
@ -110,6 +117,13 @@ public class GlossaryRepository extends EntityRepository<Glossary> {
return new GlossaryUpdater(original, updated, operation);
}
private List<EntityReference> getReviewers(Glossary entity) throws IOException {
List<String> ids =
findFrom(entity.getId(), Entity.GLOSSARY, Relationship.REVIEWS, Entity.USER, entity.getDeleted());
System.out.println("XXX reviewers for " + entity.getId() + " " + ids);
return EntityUtil.populateEntityReferences(ids, Entity.USER);
}
public static class GlossaryEntityInterface implements EntityInterface<Glossary> {
private final Glossary entity;
@ -260,5 +274,25 @@ public class GlossaryRepository extends EntityRepository<Glossary> {
public GlossaryUpdater(Glossary original, Glossary updated, Operation operation) {
super(original, updated, operation);
}
@Override
public void entitySpecificUpdate() throws IOException, ParseException {
updateReviewers(original.getEntity(), updated.getEntity());
}
private void updateReviewers(Glossary origGlossary, Glossary updatedGlossary) throws JsonProcessingException {
List<EntityReference> origUsers =
Optional.ofNullable(origGlossary.getReviewers()).orElse(Collections.emptyList());
List<EntityReference> updatedUsers =
Optional.ofNullable(updatedGlossary.getReviewers()).orElse(Collections.emptyList());
updateFromRelationships(
"reviewers",
Entity.USER,
origUsers,
updatedUsers,
Relationship.REVIEWS,
Entity.GLOSSARY,
origGlossary.getId());
}
}
}

View File

@ -16,9 +16,13 @@
package org.openmetadata.catalog.jdbi3;
import static org.openmetadata.catalog.util.EntityUtil.stringMatch;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import java.net.URI;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@ -39,8 +43,8 @@ import org.openmetadata.catalog.util.JsonUtils;
@Slf4j
public class GlossaryTermRepository extends EntityRepository<GlossaryTerm> {
private static final Fields UPDATE_FIELDS = new Fields(GlossaryResource.ALLOWED_FIELDS, "tags");
private static final Fields PATCH_FIELDS = new Fields(GlossaryResource.ALLOWED_FIELDS, "tags");
private static final Fields UPDATE_FIELDS = new Fields(GlossaryResource.ALLOWED_FIELDS, "tags,reviewers");
private static final Fields PATCH_FIELDS = new Fields(GlossaryResource.ALLOWED_FIELDS, "tags,reviewers");
public GlossaryTermRepository(CollectionDAO dao) {
super(
@ -166,11 +170,10 @@ public class GlossaryTermRepository extends EntityRepository<GlossaryTerm> {
}
for (EntityReference relTerm : Optional.ofNullable(entity.getRelatedTerms()).orElse(Collections.emptyList())) {
// Make this bidirectional relationship
addRelationship(
addBidirectionalRelationship(
entity.getId(), relTerm.getId(), Entity.GLOSSARY_TERM, Entity.GLOSSARY_TERM, Relationship.RELATED_TO);
}
for (EntityReference reviewer : Optional.ofNullable(entity.getReviewers()).orElse(Collections.emptyList())) {
// Make this bidirectional relationship
addRelationship(reviewer.getId(), entity.getId(), Entity.USER, Entity.GLOSSARY_TERM, Relationship.REVIEWS);
}
@ -349,7 +352,31 @@ public class GlossaryTermRepository extends EntityRepository<GlossaryTerm> {
@Override
public void entitySpecificUpdate() throws IOException {
// TODO
updateSynonyms(original.getEntity(), updated.getEntity());
updateReviewers(original.getEntity(), updated.getEntity());
}
private void updateSynonyms(GlossaryTerm origTerm, GlossaryTerm updatedTerm) throws JsonProcessingException {
List<String> origSynonyms = Optional.ofNullable(origTerm.getSynonyms()).orElse(Collections.emptyList());
List<String> updatedSynonyms = Optional.ofNullable(updatedTerm.getSynonyms()).orElse(Collections.emptyList());
List<String> added = new ArrayList<>();
List<String> deleted = new ArrayList<>();
recordListChange("synonyms", origSynonyms, updatedSynonyms, added, deleted, stringMatch);
}
private void updateReviewers(GlossaryTerm origTerm, GlossaryTerm updatedTerm) throws JsonProcessingException {
List<EntityReference> origUsers = Optional.ofNullable(origTerm.getReviewers()).orElse(Collections.emptyList());
List<EntityReference> updatedUsers =
Optional.ofNullable(updatedTerm.getReviewers()).orElse(Collections.emptyList());
updateFromRelationships(
"reviewers",
Entity.USER,
origUsers,
updatedUsers,
Relationship.REVIEWS,
Entity.GLOSSARY_TERM,
origTerm.getId());
}
}
}

View File

@ -13,7 +13,6 @@
package org.openmetadata.catalog.jdbi3;
import static org.openmetadata.catalog.util.EntityUtil.entityReferenceMatch;
import static org.openmetadata.catalog.util.EntityUtil.toBoolean;
import com.fasterxml.jackson.core.JsonProcessingException;
@ -63,26 +62,6 @@ public class TeamRepository extends EntityRepository<Team> {
return entityReferences;
}
public void validateEntityReferences(List<EntityReference> entityReferences, String entityType) throws IOException {
if (entityReferences != null) {
entityReferences.sort(EntityUtil.compareEntityReference);
for (EntityReference entityReference : entityReferences) {
EntityReference ref;
switch (entityType) {
case Entity.USER:
ref = daoCollection.userDAO().findEntityReferenceById(entityReference.getId());
break;
case Entity.ROLE:
ref = daoCollection.roleDAO().findEntityReferenceById(entityReference.getId());
break;
default:
throw new IllegalArgumentException("Unsupported entity reference for validation");
}
entityReference.withType(ref.getType()).withName(ref.getName()).withDisplayName(ref.getDisplayName());
}
}
}
@Override
public Team setFields(Team team, Fields fields) throws IOException {
if (!fields.contains("profile")) {
@ -107,8 +86,8 @@ public class TeamRepository extends EntityRepository<Team> {
@Override
public void prepare(Team team) throws IOException {
validateEntityReferences(team.getUsers(), Entity.USER);
validateEntityReferences(team.getDefaultRoles(), Entity.ROLE);
validateUsers(team.getUsers());
validateRoles(team.getDefaultRoles());
}
@Override
@ -297,8 +276,8 @@ public class TeamRepository extends EntityRepository<Team> {
private void updateUsers(Team origTeam, Team updatedTeam) throws JsonProcessingException {
List<EntityReference> origUsers = Optional.ofNullable(origTeam.getUsers()).orElse(Collections.emptyList());
List<EntityReference> updatedUsers = Optional.ofNullable(updatedTeam.getUsers()).orElse(Collections.emptyList());
updateEntityRelationships(
"users", origTeam.getId(), updatedTeam.getId(), Relationship.HAS, Entity.USER, origUsers, updatedUsers);
updateToRelationships(
"users", Entity.TEAM, origTeam.getId(), Relationship.HAS, Entity.USER, origUsers, updatedUsers);
}
private void updateDefaultRoles(Team origTeam, Team updatedTeam) throws JsonProcessingException {
@ -306,41 +285,14 @@ public class TeamRepository extends EntityRepository<Team> {
Optional.ofNullable(origTeam.getDefaultRoles()).orElse(Collections.emptyList());
List<EntityReference> updatedDefaultRoles =
Optional.ofNullable(updatedTeam.getDefaultRoles()).orElse(Collections.emptyList());
updateEntityRelationships(
updateToRelationships(
"defaultRoles",
Entity.TEAM,
origTeam.getId(),
updatedTeam.getId(),
Relationship.HAS,
Entity.ROLE,
origDefaultRoles,
updatedDefaultRoles);
}
private void updateEntityRelationships(
String field,
UUID origId,
UUID updatedId,
Relationship relationshipType,
String toEntityType,
List<EntityReference> origRefs,
List<EntityReference> updatedRefs)
throws JsonProcessingException {
List<EntityReference> added = new ArrayList<>();
List<EntityReference> deleted = new ArrayList<>();
if (!recordListChange(field, origRefs, updatedRefs, added, deleted, entityReferenceMatch)) {
// No changes between original and updated.
return;
}
// Remove relationships from original
daoCollection
.relationshipDAO()
.deleteFrom(origId.toString(), Entity.TEAM, relationshipType.ordinal(), toEntityType);
// Add relationships from updated
for (EntityReference ref : updatedRefs) {
addRelationship(updatedId, ref.getId(), Entity.TEAM, toEntityType, relationshipType);
}
updatedRefs.sort(EntityUtil.compareEntityReference);
origRefs.sort(EntityUtil.compareEntityReference);
}
}
}

View File

@ -172,7 +172,7 @@ public class UserRepository extends EntityRepository<User> {
.findTo(user.getId().toString(), Entity.USER, Relationship.FOLLOWS.ordinal(), toBoolean(toInclude(user))));
}
public List<EntityReference> validateRoles(List<UUID> roleIds) throws IOException {
public List<EntityReference> validateRolesByIds(List<UUID> roleIds) throws IOException {
if (roleIds == null) {
return Collections.emptyList(); // Return an empty roles list
}

View File

@ -88,6 +88,7 @@ public class GlossaryResource {
public static Glossary addHref(UriInfo uriInfo, Glossary glossary) {
glossary.setHref(RestUtil.getHref(uriInfo, COLLECTION_PATH, glossary.getId()));
Entity.withHref(uriInfo, glossary.getOwner());
Entity.withHref(uriInfo, glossary.getReviewers());
return glossary;
}
@ -110,7 +111,7 @@ public class GlossaryResource {
}
}
static final String FIELDS = "owner,tags";
static final String FIELDS = "owner,tags,reviewers";
public static final List<String> ALLOWED_FIELDS = Entity.getEntityFields(Glossary.class);
@GET

View File

@ -434,6 +434,6 @@ public class UserResource {
.withUpdatedBy(securityContext.getUserPrincipal().getName())
.withUpdatedAt(System.currentTimeMillis())
.withTeams(dao.validateTeams(create.getTeams()))
.withRoles(dao.validateRoles(create.getRoles()));
.withRoles(dao.validateRolesByIds(create.getRoles()));
}
}

View File

@ -18,10 +18,10 @@
"type": "string"
},
"reviewers": {
"description": "User names of the reviewers for this glossary.",
"description": "User references of the reviewers for this glossary.",
"type": "array",
"items": {
"type": "string"
"$ref": "../../type/entityReference.json"
}
},
"owner": {

View File

@ -2,7 +2,7 @@
"$id": "https://open-metadata.org/schema/entity/data/glossary.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Glossary",
"description": "This schema defines the Glossary entity based on SKOS.",
"description": "This schema defines the Glossary entity. A Glossary is collection of hierarchical GlossaryTerms.",
"type": "object",
"definitions": {
"name": {
@ -47,10 +47,10 @@
"$ref": "../../type/basic.json#/definitions/href"
},
"reviewers": {
"description": "User names of the reviewers for this glossary.",
"description": "User references of the reviewers for this glossary.",
"type": "array",
"items": {
"type": "string"
"$ref": "../../type/entityReference.json"
}
},
"owner": {

View File

@ -2,7 +2,7 @@
"$id": "https://open-metadata.org/schema/entity/data/glossaryTerm.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "GlossaryTerm",
"description": "This schema defines the Glossary term entities.",
"description": "This schema defines te Glossary term entities.",
"type": "object",
"definitions": {
"name": {
@ -94,10 +94,6 @@
"description": "User who made the update.",
"type": "string"
},
"skos": {
"description": "SKOS data in JSON-LD format",
"type": "string"
},
"href": {
"description": "Link to the resource corresponding to this entity.",
"$ref": "../../type/basic.json#/definitions/href"

View File

@ -1952,7 +1952,14 @@ public abstract class EntityResourceTest<T, K> extends CatalogApplicationTest {
for (EntityReference expected : expectedList) {
EntityReference actual =
actualList.stream().filter(a -> EntityUtil.entityReferenceMatch.test(a, expected)).findAny().orElse(null);
assertNotNull(actual, "Expected entity " + expected.getId() + " not found");
assertNotNull(actual, "Expected entity reference " + expected.getId() + " not found");
}
}
protected void assertStrings(List<String> expectedList, List<String> actualList) {
for (String expected : expectedList) {
String actual = actualList.stream().filter(a -> EntityUtil.stringMatch.test(a, expected)).findAny().orElse(null);
assertNotNull(actual, "Expected string " + expected + " not found");
}
}

View File

@ -22,10 +22,12 @@ import static org.openmetadata.catalog.util.TestUtils.assertListNull;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import org.apache.http.client.HttpResponseException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestMethodOrder;
import org.openmetadata.catalog.Entity;
@ -33,8 +35,12 @@ import org.openmetadata.catalog.api.data.CreateGlossary;
import org.openmetadata.catalog.entity.data.Glossary;
import org.openmetadata.catalog.jdbi3.GlossaryRepository.GlossaryEntityInterface;
import org.openmetadata.catalog.resources.EntityResourceTest;
import org.openmetadata.catalog.type.ChangeDescription;
import org.openmetadata.catalog.type.EntityReference;
import org.openmetadata.catalog.type.FieldChange;
import org.openmetadata.catalog.util.JsonUtils;
import org.openmetadata.catalog.util.TestUtils;
import org.openmetadata.catalog.util.TestUtils.UpdateType;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class GlossaryResourceTest extends EntityResourceTest<Glossary, CreateGlossary> {
@ -57,6 +63,26 @@ public class GlossaryResourceTest extends EntityResourceTest<Glossary, CreateGlo
super.setup(test);
}
@Test
void patch_addDeleteReviewers(TestInfo test) throws IOException {
CreateGlossary create = createRequest(getEntityName(test), "", "", null);
Glossary glossary = createEntity(create, ADMIN_AUTH_HEADERS);
// Add reviewer USER1 in PATCH request
String origJson = JsonUtils.pojoToJson(glossary);
glossary.withReviewers(List.of(USER_OWNER1));
ChangeDescription change = getChangeDescription(glossary.getVersion());
change.getFieldsAdded().add(new FieldChange().withName("reviewers").withNewValue(List.of(USER_OWNER1)));
glossary = patchEntityAndCheck(glossary, origJson, ADMIN_AUTH_HEADERS, UpdateType.MINOR_UPDATE, change);
// Remove a reviewer in PATCH request
origJson = JsonUtils.pojoToJson(glossary);
glossary.withReviewers(null);
change = getChangeDescription(glossary.getVersion());
change.getFieldsDeleted().add(new FieldChange().withName("reviewers").withOldValue(List.of(USER_OWNER1)));
patchEntityAndCheck(glossary, origJson, ADMIN_AUTH_HEADERS, UpdateType.MINOR_UPDATE, change);
}
@Override
public CreateGlossary createRequest(String name, String description, String displayName, EntityReference owner) {
return new CreateGlossary()
@ -91,6 +117,7 @@ public class GlossaryResourceTest extends EntityResourceTest<Glossary, CreateGlo
// Entity specific validation
TestUtils.validateTags(expected.getTags(), patched.getTags());
TestUtils.assertEntityReferenceList(expected.getReviewers(), patched.getReviewers());
}
@Override
@ -120,6 +147,13 @@ public class GlossaryResourceTest extends EntityResourceTest<Glossary, CreateGlo
if (expected == actual) {
return;
}
assertCommonFieldChange(fieldName, expected, actual);
if (fieldName.equals("reviewers")) {
@SuppressWarnings("unchecked")
List<EntityReference> expectedRefs = (List<EntityReference>) expected;
List<EntityReference> actualRefs = JsonUtils.readObjects(actual.toString(), EntityReference.class);
assertEntityReferencesFieldChange(expectedRefs, actualRefs);
} else {
assertCommonFieldChange(fieldName, expected, actual);
}
}
}

View File

@ -50,10 +50,14 @@ import org.openmetadata.catalog.entity.data.GlossaryTerm;
import org.openmetadata.catalog.jdbi3.GlossaryRepository.GlossaryEntityInterface;
import org.openmetadata.catalog.jdbi3.GlossaryTermRepository.GlossaryTermEntityInterface;
import org.openmetadata.catalog.resources.EntityResourceTest;
import org.openmetadata.catalog.type.ChangeDescription;
import org.openmetadata.catalog.type.EntityReference;
import org.openmetadata.catalog.type.FieldChange;
import org.openmetadata.catalog.util.EntityUtil;
import org.openmetadata.catalog.util.JsonUtils;
import org.openmetadata.catalog.util.ResultList;
import org.openmetadata.catalog.util.TestUtils;
import org.openmetadata.catalog.util.TestUtils.UpdateType;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class GlossaryTermResourceTest extends EntityResourceTest<GlossaryTerm, CreateGlossaryTerm> {
@ -178,6 +182,28 @@ public class GlossaryTermResourceTest extends EntityResourceTest<GlossaryTerm, C
glossaryTermMismatch(term1.getId().toString(), glossary2.getId().toString()));
}
@Test
void patch_addDeleteReviewers(TestInfo test) throws IOException {
CreateGlossaryTerm create = createRequest(getEntityName(test), "", "", null).withReviewers(null).withSynonyms(null);
GlossaryTerm term = createEntity(create, ADMIN_AUTH_HEADERS);
// Add reviewer USER1 in PATCH request
String origJson = JsonUtils.pojoToJson(term);
term.withReviewers(List.of(USER_OWNER1)).withSynonyms(List.of("synonym1"));
ChangeDescription change = getChangeDescription(term.getVersion());
change.getFieldsAdded().add(new FieldChange().withName("reviewers").withNewValue(List.of(USER_OWNER1)));
change.getFieldsAdded().add(new FieldChange().withName("synonyms").withNewValue(List.of("synonym1")));
term = patchEntityAndCheck(term, origJson, ADMIN_AUTH_HEADERS, UpdateType.MINOR_UPDATE, change);
// Remove a reviewer in PATCH request
origJson = JsonUtils.pojoToJson(term);
term.withReviewers(null).withSynonyms(null);
change = getChangeDescription(term.getVersion());
change.getFieldsDeleted().add(new FieldChange().withName("reviewers").withOldValue(List.of(USER_OWNER1)));
change.getFieldsDeleted().add(new FieldChange().withName("synonyms").withOldValue(List.of("synonym1")));
patchEntityAndCheck(term, origJson, ADMIN_AUTH_HEADERS, UpdateType.MINOR_UPDATE, change);
}
public GlossaryTerm createTerm(Glossary glossary, GlossaryTerm parent, String termName) throws HttpResponseException {
EntityReference glossaryRef = new GlossaryEntityInterface(glossary).getEntityReference();
EntityReference parentRef = parent != null ? new GlossaryTermEntityInterface(parent).getEntityReference() : null;
@ -281,6 +307,18 @@ public class GlossaryTermResourceTest extends EntityResourceTest<GlossaryTerm, C
if (expected == actual) {
return;
}
assertCommonFieldChange(fieldName, expected, actual);
if (fieldName.equals("reviewers")) {
@SuppressWarnings("unchecked")
List<EntityReference> expectedRefs = (List<EntityReference>) expected;
List<EntityReference> actualRefs = JsonUtils.readObjects(actual.toString(), EntityReference.class);
assertEntityReferencesFieldChange(expectedRefs, actualRefs);
} else if (fieldName.equals("synonyms")) {
@SuppressWarnings("unchecked")
List<String> expectedRefs = (List<String>) expected;
List<String> actualRefs = JsonUtils.readObjects(actual.toString(), String.class);
assertStrings(expectedRefs, actualRefs);
} else {
assertCommonFieldChange(fieldName, expected, actual);
}
}
}