Fixes #986 Move ownership checks to base EntityResourceTest for all the entities

This commit is contained in:
sureshms 2021-10-30 10:51:43 -07:00
parent 9fb35690e9
commit 852b64d31d
4 changed files with 59 additions and 80 deletions

View File

@ -1,7 +1,6 @@
package org.openmetadata.catalog.jdbi3; package org.openmetadata.catalog.jdbi3;
import org.jdbi.v3.sqlobject.transaction.Transaction; import org.jdbi.v3.sqlobject.transaction.Transaction;
import org.openmetadata.catalog.Entity;
import org.openmetadata.catalog.jdbi3.CollectionDAO.EntityVersionPair; import org.openmetadata.catalog.jdbi3.CollectionDAO.EntityVersionPair;
import org.openmetadata.catalog.type.ChangeDescription; import org.openmetadata.catalog.type.ChangeDescription;
import org.openmetadata.catalog.type.EntityHistory; import org.openmetadata.catalog.type.EntityHistory;
@ -251,12 +250,12 @@ public abstract class EntityRepository<T> {
if (recordChange("owner", origOwner == null ? null : origOwner.getId(), if (recordChange("owner", origOwner == null ? null : origOwner.getId(),
updatedOwner == null ? null : updatedOwner.getId())) { updatedOwner == null ? null : updatedOwner.getId())) {
EntityUtil.updateOwner(daoCollection.relationshipDAO(), origOwner, EntityUtil.updateOwner(daoCollection.relationshipDAO(), origOwner,
updatedOwner, original.getId(), Entity.TABLE); updatedOwner, original.getId(), entityName);
} }
} }
private void updateTags() throws IOException { private void updateTags() throws IOException {
// Remove current table tags in the database. It will be added back later from the merged tag list. // Remove current entity tags in the database. It will be added back later from the merged tag list.
List<TagLabel> origTags = original.getTags(); List<TagLabel> origTags = original.getTags();
List<TagLabel> updatedTags = updated.getTags(); List<TagLabel> updatedTags = updated.getTags();
EntityUtil.removeTagsByPrefix(daoCollection.tagDAO(), original.getFullyQualifiedName()); EntityUtil.removeTagsByPrefix(daoCollection.tagDAO(), original.getFullyQualifiedName());

View File

@ -193,7 +193,7 @@ public final class EntityUtil {
EntityReference owner) { EntityReference owner) {
// Add relationship owner --- owns ---> ownedEntity // Add relationship owner --- owns ---> ownedEntity
if (owner != null) { if (owner != null) {
LOG.info("Adding owner {}:{} for entity {}", owner.getType(), owner.getId(), ownedEntityId); LOG.info("Adding owner {}:{} for entity {}:{}", owner.getType(), owner.getId(), ownedEntityType, ownedEntityId);
dao.insert(owner.getId().toString(), ownedEntityId.toString(), owner.getType(), ownedEntityType, dao.insert(owner.getId().toString(), ownedEntityId.toString(), owner.getType(), ownedEntityType,
Relationship.OWNS.ordinal()); Relationship.OWNS.ordinal());
} }
@ -465,12 +465,12 @@ public final class EntityUtil {
public static String getLocalColumnName(String fqn) { public static String getLocalColumnName(String fqn) {
// Return for fqn=service.database.table.c1 -> c1 // Return for fqn=service.database.table.c1 -> c1
// Return for fqn=service.database.table.c1.c2 -> c1.c2 (note different from just the local name of the column c2) // Return for fqn=service.database.table.c1.c2 -> c1.c2 (note different from just the local name of the column c2)
String localColumnName = ""; StringBuilder localColumnName = new StringBuilder();
String[] s = fqn.split("\\."); String[] s = fqn.split("\\.");
for (int i = 3; i < s.length -1 ; i++) { for (int i = 3; i < s.length -1 ; i++) {
localColumnName += s[i] + "."; localColumnName.append(s[i]).append(".");
} }
localColumnName += s[s.length - 1]; localColumnName.append(s[s.length - 1]);
return localColumnName; return localColumnName.toString();
} }
} }

View File

@ -211,27 +211,32 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
// Ignore the test - User and team entities can't be owned like other data assets // Ignore the test - User and team entities can't be owned like other data assets
return; return;
} }
// Create an entity without owner
Object request = createRequest(test, "description", "displayName", null); Object request = createRequest(test, "description", "displayName", null);
T entity = createAndCheckEntity(request, adminAuthHeaders()); T entity = createAndCheckEntity(request, adminAuthHeaders());
EntityInterface<T> entityInterface = getEntityInterface(entity); EntityInterface<T> entityInterface = getEntityInterface(entity);
// Add TEAM_OWNER1 as owner // Set TEAM_OWNER1 as owner using PUT request
request = createRequest(test, "description", "displayName", TEAM_OWNER1); request = createRequest(test, "description", "displayName", TEAM_OWNER1);
ChangeDescription change = getChangeDescription(entityInterface.getVersion()) ChangeDescription change = getChangeDescription(entityInterface.getVersion())
.withFieldsAdded(Collections.singletonList("owner")); .withFieldsAdded(Collections.singletonList("owner"));
entity = updateAndCheckEntity(request, OK, adminAuthHeaders(), MINOR_UPDATE, change); entity = updateAndCheckEntity(request, OK, adminAuthHeaders(), MINOR_UPDATE, change);
entityInterface = getEntityInterface(entity); entityInterface = getEntityInterface(entity);
checkOwnerOwns(TEAM_OWNER1, entityInterface.getId(), true);
// Change owner from TEAM_OWNER1 to USER_OWNER1 // Change owner from TEAM_OWNER1 to USER_OWNER1 using PUT request
request = createRequest(test, "description", "displayName", USER_OWNER1); request = createRequest(test, "description", "displayName", USER_OWNER1);
change = getChangeDescription(entityInterface.getVersion()).withFieldsUpdated(Collections.singletonList("owner")); change = getChangeDescription(entityInterface.getVersion()).withFieldsUpdated(Collections.singletonList("owner"));
entity = updateAndCheckEntity(request, OK, adminAuthHeaders(), MINOR_UPDATE, change); entity = updateAndCheckEntity(request, OK, adminAuthHeaders(), MINOR_UPDATE, change);
entityInterface = getEntityInterface(entity); entityInterface = getEntityInterface(entity);
checkOwnerOwns(USER_OWNER1, entityInterface.getId(), true);
checkOwnerOwns(TEAM_OWNER1, entityInterface.getId(), false);
// Remove ownership // Remove ownership (from USER_OWNER1) using PUT request
request = createRequest(test, "description", "displayName", null); request = createRequest(test, "description", "displayName", null);
change = getChangeDescription(entityInterface.getVersion()).withFieldsDeleted(Collections.singletonList("owner")); change = getChangeDescription(entityInterface.getVersion()).withFieldsDeleted(Collections.singletonList("owner"));
updateAndCheckEntity(request, OK, adminAuthHeaders(), MINOR_UPDATE, change); updateAndCheckEntity(request, OK, adminAuthHeaders(), MINOR_UPDATE, change);
checkOwnerOwns(USER_OWNER1, entityInterface.getId(), false);
} }
@Test @Test
@ -281,38 +286,38 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Common entity functionality for tests // Common entity functionality for tests
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public final WebTarget getCollection() { protected final WebTarget getCollection() {
return getResource(collectionName); return getResource(collectionName);
} }
public final WebTarget getResource(UUID id) { protected final WebTarget getResource(UUID id) {
return getResource(collectionName + "/" + id); return getResource(collectionName + "/" + id);
} }
public final T getEntity(UUID id, Map<String, String> authHeaders) throws HttpResponseException { protected final T getEntity(UUID id, Map<String, String> authHeaders) throws HttpResponseException {
WebTarget target = getResource(id); WebTarget target = getResource(id);
target = target.queryParam("fields", allFields); target = target.queryParam("fields", allFields);
return TestUtils.get(target, entityClass, authHeaders); return TestUtils.get(target, entityClass, authHeaders);
} }
public final T createEntity(Object createRequest, Map<String, String> authHeaders) protected final T createEntity(Object createRequest, Map<String, String> authHeaders)
throws HttpResponseException { throws HttpResponseException {
return TestUtils.post(getCollection(), createRequest, entityClass, authHeaders); return TestUtils.post(getCollection(), createRequest, entityClass, authHeaders);
} }
public final T updateEntity(Object updateRequest, Status status, Map<String, String> authHeaders) protected final T updateEntity(Object updateRequest, Status status, Map<String, String> authHeaders)
throws HttpResponseException { throws HttpResponseException {
return TestUtils.put(getCollection(), updateRequest, entityClass, status, authHeaders); return TestUtils.put(getCollection(), updateRequest, entityClass, status, authHeaders);
} }
public final T patchEntity(UUID id, String originalJson, T updated, Map<String, String> authHeaders) protected final T patchEntity(UUID id, String originalJson, T updated, Map<String, String> authHeaders)
throws JsonProcessingException, HttpResponseException { throws JsonProcessingException, HttpResponseException {
String updatedTableJson = JsonUtils.pojoToJson(updated); String updatedTableJson = JsonUtils.pojoToJson(updated);
JsonPatch patch = JsonSchemaUtil.getJsonPatch(originalJson, updatedTableJson); JsonPatch patch = JsonSchemaUtil.getJsonPatch(originalJson, updatedTableJson);
return TestUtils.patch(getResource(id), patch, entityClass, authHeaders); return TestUtils.patch(getResource(id), patch, entityClass, authHeaders);
} }
public final T createAndCheckEntity(Object create, Map<String, String> authHeaders) throws HttpResponseException { protected final T createAndCheckEntity(Object create, Map<String, String> authHeaders) throws HttpResponseException {
// Validate an entity that is created has all the information set in create request // Validate an entity that is created has all the information set in create request
String updatedBy = TestUtils.getPrincipal(authHeaders); String updatedBy = TestUtils.getPrincipal(authHeaders);
T entity = createEntity(create, authHeaders); T entity = createEntity(create, authHeaders);
@ -330,7 +335,7 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
return entity; return entity;
} }
public T updateAndCheckEntity(Object request, Status status, Map<String, String> authHeaders, protected final T updateAndCheckEntity(Object request, Status status, Map<String, String> authHeaders,
UpdateType updateType, ChangeDescription changeDescription) UpdateType updateType, ChangeDescription changeDescription)
throws IOException { throws IOException {
T updated = updateEntity(request, status, authHeaders); T updated = updateEntity(request, status, authHeaders);
@ -369,7 +374,7 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
return updated; return updated;
} }
public final T patchEntityAndCheck(T updated, String originalJson, Map<String, String> authHeaders, protected final T patchEntityAndCheck(T updated, String originalJson, Map<String, String> authHeaders,
UpdateType updateType, ChangeDescription expectedChange) UpdateType updateType, ChangeDescription expectedChange)
throws JsonProcessingException, HttpResponseException { throws JsonProcessingException, HttpResponseException {
EntityInterface<T> entityInterface = getEntityInterface(updated); EntityInterface<T> entityInterface = getEntityInterface(updated);
@ -386,7 +391,7 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
return returned; return returned;
} }
public final void validateCommonEntityFields(EntityInterface<T> entity, String expectedDescription, protected final void validateCommonEntityFields(EntityInterface<T> entity, String expectedDescription,
String expectedUpdatedByUser, EntityReference expectedOwner) { String expectedUpdatedByUser, EntityReference expectedOwner) {
assertNotNull(entity.getId()); assertNotNull(entity.getId());
assertNotNull(entity.getHref()); assertNotNull(entity.getHref());
@ -396,7 +401,7 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
assertOwner(expectedOwner, entity.getOwner()); assertOwner(expectedOwner, entity.getOwner());
} }
public final void validateChangeDescription(T updated, UpdateType updateType, protected final void validateChangeDescription(T updated, UpdateType updateType,
ChangeDescription expectedChange) { ChangeDescription expectedChange) {
EntityInterface<T> updatedEntityInterface = getEntityInterface(updated); EntityInterface<T> updatedEntityInterface = getEntityInterface(updated);
if (updateType == UpdateType.CREATED) { if (updateType == UpdateType.CREATED) {
@ -414,29 +419,29 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
} }
} }
public EntityHistory getVersionList(UUID id, Map<String, String> authHeaders) throws HttpResponseException { protected EntityHistory getVersionList(UUID id, Map<String, String> authHeaders) throws HttpResponseException {
WebTarget target = getResource(collectionName + "/" + id + "/versions"); WebTarget target = getResource(collectionName + "/" + id + "/versions");
return TestUtils.get(target, EntityHistory.class, authHeaders); return TestUtils.get(target, EntityHistory.class, authHeaders);
} }
public T getVersion(UUID id, Double version, Map<String, String> authHeaders) throws HttpResponseException { protected T getVersion(UUID id, Double version, Map<String, String> authHeaders) throws HttpResponseException {
WebTarget target = getResource(collectionName + "/" + id + "/versions/" + version.toString()); WebTarget target = getResource(collectionName + "/" + id + "/versions/" + version.toString());
return TestUtils.get(target, entityClass, authHeaders); return TestUtils.get(target, entityClass, authHeaders);
} }
public void assertFieldLists(List<String> expectedList, List<String> actualList) { protected static void assertFieldLists(List<String> expectedList, List<String> actualList) {
expectedList.sort(Comparator.comparing(String::toString)); expectedList.sort(Comparator.comparing(String::toString));
actualList.sort(Comparator.comparing(String::toString)); actualList.sort(Comparator.comparing(String::toString));
assertIterableEquals(expectedList, actualList); assertIterableEquals(expectedList, actualList);
} }
public ChangeDescription getChangeDescription(Double previousVersion) { protected ChangeDescription getChangeDescription(Double previousVersion) {
return new ChangeDescription().withPreviousVersion(previousVersion) return new ChangeDescription().withPreviousVersion(previousVersion)
.withFieldsAdded(new ArrayList<>()).withFieldsUpdated(new ArrayList<>()) .withFieldsAdded(new ArrayList<>()).withFieldsUpdated(new ArrayList<>())
.withFieldsDeleted(new ArrayList<>()); .withFieldsDeleted(new ArrayList<>());
} }
public static void assertOwner(EntityReference expected, EntityReference actual) { protected static void assertOwner(EntityReference expected, EntityReference actual) {
if (expected != null) { if (expected != null) {
TestUtils.validateEntityReference(actual); TestUtils.validateEntityReference(actual);
assertEquals(expected.getId(), actual.getId()); assertEquals(expected.getId(), actual.getId());
@ -445,9 +450,36 @@ public abstract class EntityResourceTest<T> extends CatalogApplicationTest {
assertNull(actual); assertNull(actual);
} }
} }
public static void assertService(EntityReference expected, EntityReference actual) { protected static void assertService(EntityReference expected, EntityReference actual) {
TestUtils.validateEntityReference(actual); TestUtils.validateEntityReference(actual);
assertEquals(expected.getId(), actual.getId()); assertEquals(expected.getId(), actual.getId());
assertEquals(expected.getType(), actual.getType()); assertEquals(expected.getType(), actual.getType());
} }
protected static void checkOwnerOwns(EntityReference owner, UUID entityId, boolean expectedOwning)
throws HttpResponseException {
if (owner != null) {
UUID ownerId = owner.getId();
List<EntityReference> ownsList;
if (owner.getType().equals(Entity.USER)) {
User user = UserResourceTest.getUser(ownerId, "owns", adminAuthHeaders());
ownsList = user.getOwns();
} else if (owner.getType().equals(Entity.TEAM)) {
Team team = TeamResourceTest.getTeam(ownerId, "owns", adminAuthHeaders());
ownsList = team.getOwns();
} else {
throw new IllegalArgumentException("Invalid owner type " + owner.getType());
}
boolean owning = false;
for (EntityReference owns : ownsList) {
TestUtils.validateEntityReference(owns);
if (owns.getId().equals(entityId)) {
owning = true;
break;
}
}
assertEquals(expectedOwning, owning, "Ownership not correct in the owns list for " + owner.getType());
}
}
} }

View File

@ -31,7 +31,6 @@ import org.openmetadata.catalog.api.data.CreateTable;
import org.openmetadata.catalog.entity.data.Database; import org.openmetadata.catalog.entity.data.Database;
import org.openmetadata.catalog.entity.data.Table; import org.openmetadata.catalog.entity.data.Table;
import org.openmetadata.catalog.entity.services.DatabaseService; import org.openmetadata.catalog.entity.services.DatabaseService;
import org.openmetadata.catalog.entity.teams.Team;
import org.openmetadata.catalog.entity.teams.User; import org.openmetadata.catalog.entity.teams.User;
import org.openmetadata.catalog.exception.CatalogExceptionMessage; import org.openmetadata.catalog.exception.CatalogExceptionMessage;
import org.openmetadata.catalog.jdbi3.TableRepository.TableEntityInterface; import org.openmetadata.catalog.jdbi3.TableRepository.TableEntityInterface;
@ -39,7 +38,6 @@ import org.openmetadata.catalog.resources.EntityResourceTest;
import org.openmetadata.catalog.resources.databases.TableResource.TableList; import org.openmetadata.catalog.resources.databases.TableResource.TableList;
import org.openmetadata.catalog.resources.services.DatabaseServiceResourceTest; import org.openmetadata.catalog.resources.services.DatabaseServiceResourceTest;
import org.openmetadata.catalog.resources.tags.TagResourceTest; import org.openmetadata.catalog.resources.tags.TagResourceTest;
import org.openmetadata.catalog.resources.teams.TeamResourceTest;
import org.openmetadata.catalog.resources.teams.UserResourceTest; import org.openmetadata.catalog.resources.teams.UserResourceTest;
import org.openmetadata.catalog.type.ChangeDescription; import org.openmetadata.catalog.type.ChangeDescription;
import org.openmetadata.catalog.type.Column; import org.openmetadata.catalog.type.Column;
@ -385,30 +383,6 @@ public class TableResourceTest extends EntityResourceTest<Table> {
assertResponse(exception, FORBIDDEN, "Principal: CatalogPrincipal{name='test'} is not admin"); assertResponse(exception, FORBIDDEN, "Principal: CatalogPrincipal{name='test'} is not admin");
} }
@Test
public void put_tableOwnershipUpdate_200(TestInfo test) throws IOException {
CreateTable request = create(test).withOwner(USER_OWNER1).withDescription("description");
Table table = createAndCheckEntity(request, adminAuthHeaders());
checkOwnerOwns(USER_OWNER1, table.getId(), true);
// Change ownership from USER_OWNER1 to TEAM_OWNER1
ChangeDescription change = getChangeDescription(table.getVersion());
change.getFieldsUpdated().add("owner");
Table updatedTable = updateAndCheckEntity(request.withOwner(TEAM_OWNER1), OK, adminAuthHeaders(),
MINOR_UPDATE, change);
checkOwnerOwns(USER_OWNER1, updatedTable.getId(), false);
checkOwnerOwns(TEAM_OWNER1, updatedTable.getId(), true);
// Remove ownership
change = getChangeDescription(updatedTable.getVersion());
change.getFieldsDeleted().add("owner");
updatedTable = updateAndCheckEntity(request.withOwner(null), OK, adminAuthHeaders(), MINOR_UPDATE,
change);
assertNull(updatedTable.getOwner());
checkOwnerOwns(TEAM_OWNER1, updatedTable.getId(), false);
}
@Test @Test
public void put_tableTableConstraintUpdate_200(TestInfo test) throws IOException { public void put_tableTableConstraintUpdate_200(TestInfo test) throws IOException {
// Create table without table constraints // Create table without table constraints
@ -1355,32 +1329,6 @@ public class TableResourceTest extends EntityResourceTest<Table> {
return getTable; return getTable;
} }
private static void checkOwnerOwns(EntityReference owner, UUID tableId, boolean expectedOwning)
throws HttpResponseException {
if (owner != null) {
UUID ownerId = owner.getId();
List<EntityReference> ownsList;
if (owner.getType().equals(Entity.USER)) {
User user = UserResourceTest.getUser(ownerId, "owns", adminAuthHeaders());
ownsList = user.getOwns();
} else if (owner.getType().equals(Entity.TEAM)) {
Team team = TeamResourceTest.getTeam(ownerId, "owns", adminAuthHeaders());
ownsList = team.getOwns();
} else {
throw new IllegalArgumentException("Invalid owner type " + owner.getType());
}
boolean owning = false;
for (EntityReference owns : ownsList) {
TestUtils.validateEntityReference(owns);
if (owns.getId().equals(tableId)) {
owning = true;
break;
}
}
assertEquals(expectedOwning, owning, "Ownership not correct in the owns list for " + owner.getType());
}
}
private static int getTagUsageCount(String tagFQN, Map<String, String> authHeaders) throws HttpResponseException { private static int getTagUsageCount(String tagFQN, Map<String, String> authHeaders) throws HttpResponseException {
return TagResourceTest.getTag(tagFQN, "usageCount", authHeaders).getUsageCount(); return TagResourceTest.getTag(tagFQN, "usageCount", authHeaders).getUsageCount();