diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/CollectionDAO.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/CollectionDAO.java index d8fae2613b8..fc9b28f4ff7 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/CollectionDAO.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/CollectionDAO.java @@ -247,13 +247,13 @@ public interface CollectionDAO { // @SqlQuery("SELECT toId, toEntity FROM entity_relationship " + "WHERE fromId = :fromId AND relation = :relation " + - "ORDER BY fromId") + "ORDER BY toId") @RegisterRowMapper(ToEntityReferenceMapper.class) List findTo(@Bind("fromId") String fromId, @Bind("relation") int relation); @SqlQuery("SELECT toId FROM entity_relationship " + "WHERE fromId = :fromId AND relation = :relation AND toEntity = :toEntity " + - "ORDER BY fromId") + "ORDER BY toId") List findTo(@Bind("fromId") String fromId, @Bind("relation") int relation, @Bind("toEntity") String toEntity); diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/TeamRepository.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/TeamRepository.java index 0c0bc1bda45..61ddb306ba9 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/TeamRepository.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/TeamRepository.java @@ -31,9 +31,11 @@ import org.openmetadata.catalog.util.EntityUtil.Fields; import org.openmetadata.catalog.util.JsonUtils; import java.io.IOException; +import java.net.URI; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Optional; @@ -194,6 +196,9 @@ public class TeamRepository extends EntityRepository { @Override public Date getUpdatedAt() { return entity.getUpdatedAt(); } + @Override + public URI getHref() { return entity.getHref(); } + @Override public EntityReference getEntityReference() { return new EntityReference().withId(getId()).withName(getFullyQualifiedName()).withDescription(getDescription()) @@ -226,6 +231,9 @@ public class TeamRepository extends EntityRepository { entity.setChangeDescription(changeDescription); } + @Override + public ChangeDescription getChangeDescription() { return entity.getChangeDescription(); } + @Override public void setTags(List tags) { } } @@ -244,18 +252,26 @@ public class TeamRepository extends EntityRepository { if (updated.getEntity().getDeleted() != original.getEntity().getDeleted()) { throw new IllegalArgumentException(CatalogExceptionMessage.readOnlyAttribute("Team", "deleted")); } + recordChange("profile", original.getEntity().getProfile(), updated.getEntity().getProfile()); updateUsers(original.getEntity(), updated.getEntity()); } private void updateUsers(Team origTeam, Team updatedTeam) { + List origUsers = Optional.ofNullable(origTeam.getUsers()).orElse(Collections.emptyList()); + List updatedUsers = Optional.ofNullable(updatedTeam.getUsers()).orElse(Collections.emptyList()); + // Remove users from original and add users from updated dao.relationshipDAO().deleteFrom(origTeam.getId().toString(), Relationship.CONTAINS.ordinal(), "user"); - - for (EntityReference user : Optional.ofNullable(updatedTeam.getUsers()).orElse(Collections.emptyList())) { + // Add relationships + for (EntityReference user : updatedUsers) { dao.relationshipDAO().insert(updatedTeam.getId().toString(), user.getId().toString(), "team", "user", Relationship.CONTAINS.ordinal()); } - recordChange("users", origTeam.getUsers(), updatedTeam.getUsers()); + + // Sort by user Id as string (as done in the database) + updatedUsers.sort(Comparator.comparing(entityReference -> entityReference.getId().toString())); + origUsers.sort(Comparator.comparing(entityReference -> entityReference.getId().toString())); + recordChange("users", origUsers.isEmpty() ? null : origUsers, updatedUsers.isEmpty() ? null : updatedUsers); } } } diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/UserRepository.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/UserRepository.java index 9aced7f3ac3..1af1831789b 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/UserRepository.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/jdbi3/UserRepository.java @@ -176,7 +176,6 @@ public class UserRepository extends EntityRepository { private void assignTeams(User user, List teams) { // Query - add team to the user teams = Optional.ofNullable(teams).orElse(Collections.emptyList()); - teams.sort(Comparator.comparing(EntityReference::getId)); // Sort team order to the query order for (EntityReference team : teams) { dao.relationshipDAO().insert(team.getId().toString(), user.getId().toString(), "team", "user", CONTAINS.ordinal()); @@ -301,19 +300,13 @@ public class UserRepository extends EntityRepository { dao.relationshipDAO().deleteTo(origUser.getId().toString(), CONTAINS.ordinal(), "team"); assignTeams(updatedUser, updatedUser.getTeams()); - List origTeams = origUser.getTeams(); - List updatedTeams = updatedUser.getTeams(); - if (origTeams == null || origTeams.isEmpty()) { - origTeams = null; - } else { - origTeams.sort(Comparator.comparing(EntityReference::getId)); - } - if (updatedTeams == null || updatedTeams.isEmpty()) { - updatedTeams = null; - } else { - updatedTeams.sort(Comparator.comparing(EntityReference::getId)); - } - recordChange("teams", origTeams, updatedTeams); + List origTeams = Optional.ofNullable(origUser.getTeams()).orElse(Collections.emptyList()); + List updatedTeams = Optional.ofNullable(updatedUser.getTeams()).orElse(Collections.emptyList()); + + // Sort by team Id as string (as done in the database) + origTeams.sort(Comparator.comparing(entityReference -> entityReference.getId().toString())); + updatedTeams.sort(Comparator.comparing(entityReference -> entityReference.getId().toString())); + recordChange("teams", origTeams.isEmpty() ? null : origTeams, updatedTeams.isEmpty() ? null : updatedTeams); } } } diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/teams/TeamResource.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/teams/TeamResource.java index 038f1b3f81e..a3969cb69c7 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/teams/TeamResource.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/teams/TeamResource.java @@ -34,6 +34,7 @@ import org.openmetadata.catalog.jdbi3.TeamRepository; import org.openmetadata.catalog.resources.Collection; import org.openmetadata.catalog.security.CatalogAuthorizer; import org.openmetadata.catalog.security.SecurityUtil; +import org.openmetadata.catalog.type.EntityHistory; import org.openmetadata.catalog.type.EntityReference; import org.openmetadata.catalog.util.EntityUtil; import org.openmetadata.catalog.util.RestUtil; @@ -48,6 +49,7 @@ import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -106,7 +108,7 @@ public class TeamResource { } } - private static final String FIELDS = "profile,users,owns"; + public static final String FIELDS = "profile,users,owns"; public static final List FIELD_LIST = Arrays.asList(FIELDS.replaceAll(" ", "") .split(",")); @GET @@ -150,6 +152,22 @@ public class TeamResource { return teams; } + @GET + @Path("/{id}/versions") + @Operation(summary = "List team versions", tags = "teams", + description = "Get a list of all the versions of a team identified by `id`", + responses = {@ApiResponse(responseCode = "200", description = "List of team versions", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = EntityHistory.class))) + }) + public EntityHistory listVersions(@Context UriInfo uriInfo, + @Context SecurityContext securityContext, + @Parameter(description = "team Id", schema = @Schema(type = "string")) + @PathParam("id") String id) + throws IOException, ParseException, GeneralSecurityException { + return dao.listVersions(id); + } + @GET @Valid @Path("/{id}") @@ -192,6 +210,27 @@ public class TeamResource { return addHref(uriInfo, dao.getByName(name, fields)); } + @GET + @Path("/{id}/versions/{version}") + @Operation(summary = "Get a version of the team", tags = "teams", + description = "Get a version of the team by given `id`", + responses = { + @ApiResponse(responseCode = "200", description = "team", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = Team.class))), + @ApiResponse(responseCode = "404", description = "Team for instance {id} and version {version} is " + + "not found") + }) + public Team getVersion(@Context UriInfo uriInfo, + @Context SecurityContext securityContext, + @Parameter(description = "Team Id", schema = @Schema(type = "string")) + @PathParam("id") String id, + @Parameter(description = "Team version number in the form `major`.`minor`", + schema = @Schema(type = "string", example = "0.1 or 1.1")) + @PathParam("version") String version) throws IOException, ParseException { + return dao.getVersion(id, version); + } + @POST @Operation(summary = "Create a team", tags = "teams", description = "Create a new team.", @@ -205,15 +244,30 @@ public class TeamResource { @Context SecurityContext securityContext, @Valid CreateTeam ct) throws IOException, ParseException { SecurityUtil.checkAdminOrBotRole(authorizer, securityContext); - Team team = new Team().withId(UUID.randomUUID()).withName(ct.getName()).withDescription(ct.getDescription()) - .withDisplayName(ct.getDisplayName()).withProfile(ct.getProfile()) - .withUpdatedBy(securityContext.getUserPrincipal().getName()) - .withUpdatedAt(new Date()) - .withUsers(dao.getUsers(ct.getUsers())); + Team team = getTeam(ct, securityContext); addHref(uriInfo, dao.create(team)); return Response.created(team.getHref()).entity(team).build(); } + @PUT + @Operation(summary = "Create or Update a team", tags = "teams", + description = "Create or Update a team.", + responses = { + @ApiResponse(responseCode = "200", description = "The team ", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = CreateTeam.class))), + @ApiResponse(responseCode = "400", description = "Bad request") + }) + public Response createOrUpdateTeam(@Context UriInfo uriInfo, + @Context SecurityContext securityContext, + @Valid CreateTeam ct) throws IOException, ParseException { + SecurityUtil.checkAdminOrBotRole(authorizer, securityContext); + Team team = getTeam(ct, securityContext); + RestUtil.PutResponse response = dao.createOrUpdate(team); + team = addHref(uriInfo, response.getEntity()); + return Response.status(response.getStatus()).entity(team).build(); + } + @PATCH @Valid @Path("/{id}") @@ -252,4 +306,12 @@ public class TeamResource { dao.delete(UUID.fromString(id)); return Response.ok().build(); } + + private Team getTeam(CreateTeam ct, SecurityContext securityContext) throws IOException { + return new Team().withId(UUID.randomUUID()).withName(ct.getName()).withDescription(ct.getDescription()) + .withDisplayName(ct.getDisplayName()).withProfile(ct.getProfile()) + .withUpdatedBy(securityContext.getUserPrincipal().getName()) + .withUpdatedAt(new Date()) + .withUsers(dao.getUsers(ct.getUsers())); + } } \ No newline at end of file diff --git a/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/teams/UserResource.java b/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/teams/UserResource.java index 00c9c6d1bde..47e2a24226e 100644 --- a/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/teams/UserResource.java +++ b/catalog-rest-service/src/main/java/org/openmetadata/catalog/resources/teams/UserResource.java @@ -176,7 +176,6 @@ public class UserResource { return dao.listVersions(id); } - @GET @Valid @Path("/{id}") diff --git a/catalog-rest-service/src/main/resources/json/schema/entity/teams/team.json b/catalog-rest-service/src/main/resources/json/schema/entity/teams/team.json index bf8170adbbc..31615f3804c 100644 --- a/catalog-rest-service/src/main/resources/json/schema/entity/teams/team.json +++ b/catalog-rest-service/src/main/resources/json/schema/entity/teams/team.json @@ -55,7 +55,8 @@ }, "users" : { "description": "Users that are part of the team.", - "$ref": "../../type/entityReference.json#/definitions/entityReferenceList" + "$ref": "../../type/entityReference.json#/definitions/entityReferenceList", + "default": null }, "owns" : { "description": "List of entities owned by the team.", diff --git a/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/teams/TeamResourceTest.java b/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/teams/TeamResourceTest.java index 4e9f28f7217..1ce26bae641 100644 --- a/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/teams/TeamResourceTest.java +++ b/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/teams/TeamResourceTest.java @@ -26,15 +26,18 @@ import org.openmetadata.catalog.api.teams.CreateTeam; import org.openmetadata.catalog.entity.teams.Team; import org.openmetadata.catalog.entity.teams.User; import org.openmetadata.catalog.exception.CatalogExceptionMessage; +import org.openmetadata.catalog.jdbi3.TeamRepository.TeamEntityInterface; import org.openmetadata.catalog.jdbi3.UserRepository.UserEntityInterface; +import org.openmetadata.catalog.resources.EntityResourceTest; import org.openmetadata.catalog.resources.databases.TableResourceTest; import org.openmetadata.catalog.resources.teams.TeamResource.TeamList; +import org.openmetadata.catalog.type.ChangeDescription; import org.openmetadata.catalog.type.EntityReference; import org.openmetadata.catalog.type.ImageList; import org.openmetadata.catalog.type.Profile; +import org.openmetadata.catalog.util.EntityInterface; import org.openmetadata.catalog.util.JsonUtils; import org.openmetadata.catalog.util.TestUtils; -import org.openmetadata.catalog.util.TestUtils.UpdateType; import org.openmetadata.common.utils.JsonSchemaUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,6 +48,7 @@ import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; @@ -62,17 +66,20 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.openmetadata.catalog.exception.CatalogExceptionMessage.entityNotFound; import static org.openmetadata.catalog.resources.teams.UserResourceTest.createUser; import static org.openmetadata.catalog.util.TestUtils.UpdateType.MINOR_UPDATE; -import static org.openmetadata.catalog.util.TestUtils.UpdateType.NO_CHANGE; import static org.openmetadata.catalog.util.TestUtils.adminAuthHeaders; import static org.openmetadata.catalog.util.TestUtils.assertEntityPagination; import static org.openmetadata.catalog.util.TestUtils.assertResponse; import static org.openmetadata.catalog.util.TestUtils.authHeaders; import static org.openmetadata.catalog.util.TestUtils.validateEntityReference; -public class TeamResourceTest extends CatalogApplicationTest { +public class TeamResourceTest extends EntityResourceTest { private static final Logger LOG = LoggerFactory.getLogger(TeamResourceTest.class); final Profile PROFILE = new Profile().withImages(new ImageList().withImage(URI.create("http://image.com"))); + public TeamResourceTest() { + super(Team.class, "teams", TeamResource.FIELDS); + } + @Test public void post_teamWithLongName_400_badRequest(TestInfo test) { // Create team with mandatory name field empty @@ -102,19 +109,19 @@ public class TeamResourceTest extends CatalogApplicationTest { public void post_validTeams_as_admin_200_OK(TestInfo test) throws HttpResponseException { // Create team with different optional fields CreateTeam create = create(test, 1); - createAndCheckTeam(create, adminAuthHeaders()); + createAndCheckEntity(create, adminAuthHeaders()); create = create(test, 2).withDisplayName("displayName"); - createAndCheckTeam(create, adminAuthHeaders()); + createAndCheckEntity(create, adminAuthHeaders()); create = create(test, 3).withDescription("description"); - createAndCheckTeam(create, adminAuthHeaders()); + createAndCheckEntity(create, adminAuthHeaders()); create = create(test, 4).withProfile(PROFILE); - createAndCheckTeam(create, adminAuthHeaders()); + createAndCheckEntity(create, adminAuthHeaders()); create = create(test, 5).withDisplayName("displayName").withDescription("description").withProfile(PROFILE); - createAndCheckTeam(create, adminAuthHeaders()); + createAndCheckEntity(create, adminAuthHeaders()); } @Test @@ -122,7 +129,7 @@ public class TeamResourceTest extends CatalogApplicationTest { // Create team with different optional fields Map authHeaders = authHeaders("test@open-metadata.org"); CreateTeam create = create(test, 1); - HttpResponseException exception = assertThrows(HttpResponseException.class, () -> createAndCheckTeam(create, + HttpResponseException exception = assertThrows(HttpResponseException.class, () -> createAndCheckEntity(create, authHeaders)); assertResponse(exception, FORBIDDEN, "Principal: CatalogPrincipal{name='test'} is not admin"); } @@ -137,7 +144,7 @@ public class TeamResourceTest extends CatalogApplicationTest { List users = Arrays.asList(user1.getId(), user2.getId()); CreateTeam create = create(test).withDisplayName("displayName").withDescription("description") .withProfile(PROFILE).withUsers(users); - Team team = createAndCheckTeam(create, adminAuthHeaders()); + Team team = createAndCheckEntity(create, adminAuthHeaders()); // Make sure the user entity has relationship to the team user1 = UserResourceTest.getUser(user1.getId(), "teams", authHeaders("test@open-metadata.org")); @@ -293,7 +300,7 @@ public class TeamResourceTest extends CatalogApplicationTest { User user1 = createUser(UserResourceTest.create(test, 1), adminAuthHeaders()); List users = Collections.singletonList(user1.getId()); CreateTeam create = create(test).withUsers(users); - Team team = createAndCheckTeam(create, adminAuthHeaders()); + Team team = createAndCheckEntity(create, adminAuthHeaders()); deleteTeam(team.getId(), adminAuthHeaders()); // Make sure team is no longer there @@ -312,7 +319,7 @@ public class TeamResourceTest extends CatalogApplicationTest { authHeaders("test@open-metadata.org")); List users = Collections.singletonList(user1.getId()); CreateTeam create = create(test).withUsers(users); - Team team = createAndCheckTeam(create, adminAuthHeaders()); + Team team = createAndCheckEntity(create, adminAuthHeaders()); HttpResponseException exception = assertThrows(HttpResponseException.class, () -> deleteTeam(team.getId(), authHeaders("test@open-metadata.org"))); assertResponse(exception, FORBIDDEN, "Principal: CatalogPrincipal{name='test'} is not admin"); @@ -340,7 +347,9 @@ public class TeamResourceTest extends CatalogApplicationTest { @Test public void patch_teamAttributes_as_admin_200_ok(TestInfo test) throws HttpResponseException, JsonProcessingException { + // // Create table without any attributes + // Team team = createTeam(create(test), adminAuthHeaders()); assertNull(team.getDisplayName()); assertNull(team.getDescription()); @@ -356,80 +365,50 @@ public class TeamResourceTest extends CatalogApplicationTest { new UserEntityInterface(user2).getEntityReference()); Profile profile = new Profile().withImages(new ImageList().withImage(URI.create("http://image.com"))); + // // Add previously absent attributes - team = patchTeamAttributesAndCheck(team, "displayName", "description", profile, users, - adminAuthHeaders(), MINOR_UPDATE); + // + String originalJson = JsonUtils.pojoToJson(team); + team.withDisplayName("displayName").withDescription("description").withProfile(profile).withUsers(users); + ChangeDescription change = getChangeDescription(team.getVersion()) + .withFieldsAdded(Arrays.asList("displayName", "description", "profile", "users")); + team = patchEntityAndCheck(team, originalJson, adminAuthHeaders(), MINOR_UPDATE, change); + team.getUsers().get(0).setHref(null); + team.getUsers().get(1).setHref(null); + // // Replace the attributes + // users = Arrays.asList(new UserEntityInterface(user1).getEntityReference(), new UserEntityInterface(user3).getEntityReference()); // user2 dropped and user3 is added profile = new Profile().withImages(new ImageList().withImage(URI.create("http://image1.com"))); - team = patchTeamAttributesAndCheck(team, "displayName1", "description1", profile, users, - adminAuthHeaders(), MINOR_UPDATE); + + originalJson = JsonUtils.pojoToJson(team); + team.withDisplayName("displayName1").withDescription("description1").withProfile(profile).withUsers(users); + change = getChangeDescription(team.getVersion()) + .withFieldsUpdated(Arrays.asList("displayName", "description", "profile", "users")); + team = patchEntityAndCheck(team, originalJson, adminAuthHeaders(), MINOR_UPDATE, change); // Remove the attributes - patchTeamAttributesAndCheck(team, null, null, null, null, - adminAuthHeaders(), MINOR_UPDATE); + originalJson = JsonUtils.pojoToJson(team); + team.withDisplayName(null).withDescription(null).withProfile(null).withUsers(null); + change = getChangeDescription(team.getVersion()) + .withFieldsDeleted(Arrays.asList("displayName", "description", "profile", "users")); + patchEntityAndCheck(team, originalJson, adminAuthHeaders(), MINOR_UPDATE, change); } @Test - public void patch_teamAttributes_as_non_admin_401(TestInfo test) throws HttpResponseException { + public void patch_teamAttributes_as_non_admin_403(TestInfo test) throws HttpResponseException, + JsonProcessingException { // Create table without any attributes Team team = createTeam(create(test), adminAuthHeaders()); - assertNull(team.getDisplayName()); - assertNull(team.getDescription()); - assertNull(team.getProfile()); - assertNull(team.getDeleted()); - assertNull(team.getUsers()); - - User user1 = createUser(UserResourceTest.create(test, 1), authHeaders("test@open-metadata.org")); - User user2 = createUser(UserResourceTest.create(test, 2), authHeaders("test@open-metadata.org")); - User user3 = createUser(UserResourceTest.create(test, 3), authHeaders("test@open-metadata.org")); - - List users = Arrays.asList(new UserEntityInterface(user1).getEntityReference(), - new UserEntityInterface(user2).getEntityReference(), - new UserEntityInterface(user3).getEntityReference()); - - Profile profile = new Profile().withImages(new ImageList().withImage(URI.create("http://image.com"))); - + // Patching as a non-admin should is disallowed + String originalJson = JsonUtils.pojoToJson(team); + team.setDisplayName("newDisplayName"); HttpResponseException exception = assertThrows(HttpResponseException.class, () -> - patchTeamAttributesAndCheck(team, "displayName", "description", profile, users, - authHeaders("test@open-metadata.org"), NO_CHANGE)); + patchTeam(team.getId(), originalJson, team, authHeaders("test@open-metadata.org"))); assertResponse(exception, FORBIDDEN, "Principal: CatalogPrincipal{name='test'} is not admin"); } -// @Test -// public void patch_updateInvalidUsers_404_notFound(TestInfo test) throws HttpResponseException { -// CreateTeam create = create(test); -// Team team = createAndCheckTeam(create); -// -// // User patch to add team to user relationship to an invalid user -// List users = Collections.singletonList(UUID.randomUUID() /* invalid userId */); -// UpdateTeam update = new UpdateTeam().withUsers(users); -// HttpResponseException exception = assertThrows(HttpResponseException.class, () -> -// updateTeam(team.getId(), update)); -// assertEquals(Response.Status.NOT_FOUND.getStatusCode(), exception.getStatusCode()); -// } - - public static Team createAndCheckTeam(CreateTeam create, Map authHeaders) - throws HttpResponseException { - String updatedBy = TestUtils.getPrincipal(authHeaders); - Team team = createTeam(create, authHeaders); - assertEquals(0.1, team.getVersion()); - List expectedUsers = new ArrayList<>(); - for (UUID teamId : Optional.ofNullable(create.getUsers()).orElse(Collections.emptyList())) { - expectedUsers.add(new EntityReference().withId(teamId).withType(Entity.USER)); - } - - assertEquals(team.getName(), create.getName()); - validateTeam(team, create.getDescription(), create.getDisplayName(), create.getProfile(), expectedUsers, updatedBy); - - // Get the newly created team and validate it - Team getTeam = getTeam(team.getId(), "profile,users", authHeaders); - assertEquals(team.getName(), create.getName()); - validateTeam(getTeam, create.getDescription(), create.getDisplayName(), create.getProfile(), expectedUsers, - updatedBy); - return team; - } public static Team createTeam(CreateTeam create, Map authHeaders) throws HttpResponseException { return TestUtils.post(CatalogApplicationTest.getResource("teams"), create, Team.class, authHeaders); @@ -521,30 +500,6 @@ public class TeamResourceTest extends CatalogApplicationTest { return patchTeam(updated.getId(), originalJson, updated, authHeaders); } - private Team patchTeamAttributesAndCheck(Team before, String displayName, String description, Profile profile, - List users, Map authHeaders, UpdateType updateType) - throws JsonProcessingException, HttpResponseException { - String updatedBy = TestUtils.getPrincipal(authHeaders); - Optional.ofNullable(before.getUsers()).orElse(Collections.emptyList()).forEach(t -> t.setHref(null)); // Remove href - String tableJson = JsonUtils.pojoToJson(before); - - // Update the table attributes - before.setDisplayName(displayName); - before.setDescription(description); - before.setProfile(profile); - before.setUsers(users); - - // Validate information returned in patch response has the updates - Team updatedTeam = patchTeam(tableJson, before, authHeaders); - validateTeam(updatedTeam, description, displayName, profile, users, updatedBy); - TestUtils.validateUpdate(before.getVersion(), updatedTeam.getVersion(), updateType); - - // GET the table and Validate information returned - Team getTeam = getTeam(before.getId(), "users,profile", authHeaders); - validateTeam(getTeam, description, displayName, profile, users, updatedBy); - return getTeam; - } - public void deleteTeam(UUID id, Map authHeaders) throws HttpResponseException { TestUtils.delete(CatalogApplicationTest.getResource("teams/" + id), authHeaders); } @@ -560,4 +515,71 @@ public class TeamResourceTest extends CatalogApplicationTest { public static String getTeamName(TestInfo test) { return String.format("team_%s", test.getDisplayName()); } + + @Override + public Object createRequest(TestInfo test, String description, String displayName, EntityReference owner) { + return create(test).withDescription(description).withDisplayName(displayName); + } + + @Override + public void validateCreatedEntity(Team team, Object request, Map authHeaders) { + CreateTeam createRequest = (CreateTeam) request; + validateCommonEntityFields(getEntityInterface(team), createRequest.getDescription(), + TestUtils.getPrincipal(authHeaders), null); + + assertEquals(createRequest.getDisplayName(), team.getDisplayName()); + assertEquals(createRequest.getProfile(), team.getProfile()); + + List expectedUsers = new ArrayList<>(); + for (UUID teamId : Optional.ofNullable(createRequest.getUsers()).orElse(Collections.emptyList())) { + expectedUsers.add(new EntityReference().withId(teamId).withType(Entity.USER)); + } + List actualUsers = Optional.ofNullable(team.getUsers()).orElse(Collections.emptyList()); + if (!expectedUsers.isEmpty()) { + assertEquals(expectedUsers.size(), actualUsers.size()); + for (EntityReference user : actualUsers) { + TestUtils.validateEntityReference(user); + boolean foundUser = false; + for (EntityReference expectedEntity : expectedUsers) { + if (expectedEntity.getId().equals(user.getId())) { + foundUser = true; + break; + } + } + assertTrue(foundUser); + } + } + TestUtils.validateEntityReference(team.getOwns()); + } + + @Override + public void validateUpdatedEntity(Team updatedEntity, Object request, Map authHeaders) { + validateCreatedEntity(updatedEntity, request, authHeaders); + } + + @Override + public void validatePatchedEntity(Team expected, Team updated, Map authHeaders) { + validateCommonEntityFields(getEntityInterface(updated), expected.getDescription(), + TestUtils.getPrincipal(authHeaders), null); + + assertEquals(expected.getDisplayName(), updated.getDisplayName()); + assertEquals(expected.getProfile(), updated.getProfile()); + + List expectedUsers = Optional.ofNullable(expected.getUsers()).orElse(Collections.emptyList()); + List actualUsers = Optional.ofNullable(updated.getUsers()).orElse(Collections.emptyList()); + actualUsers.forEach(TestUtils::validateEntityReference); + actualUsers.forEach(user -> user.setHref(null)); + + // Note ordering is same as server side ordering by ID as string + // Patch requests work only if the same ordering of users on the server side + actualUsers.sort(Comparator.comparing(entityReference -> entityReference.getId().toString())); + expectedUsers.sort(Comparator.comparing(entityReference -> entityReference.getId().toString())); + assertEquals(expectedUsers, actualUsers); + TestUtils.validateEntityReference(updated.getOwns()); + } + + @Override + public EntityInterface getEntityInterface(Team entity) { + return new TeamEntityInterface(entity); + } } diff --git a/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/teams/UserResourceTest.java b/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/teams/UserResourceTest.java index 752e1c96bc0..612102c068d 100644 --- a/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/teams/UserResourceTest.java +++ b/catalog-rest-service/src/test/java/org/openmetadata/catalog/resources/teams/UserResourceTest.java @@ -448,7 +448,9 @@ public class UserResourceTest extends EntityResourceTest { .withFieldsUpdated(Arrays.asList("teams", "timezone", "profile", "isBot")); user = patchEntityAndCheck(user, origJson, adminAuthHeaders(), MINOR_UPDATE, change); + // // Remove the attributes + // origJson = JsonUtils.pojoToJson(user); user.withTeams(null).withTimezone(null).withDisplayName(null).withProfile(null) .withIsBot(null).withIsAdmin(false); @@ -654,8 +656,10 @@ public class UserResourceTest extends EntityResourceTest { updatedTeams.forEach(TestUtils::validateEntityReference); - expectedTeams.sort(Comparator.comparing(EntityReference::getName)); - updatedTeams.sort(Comparator.comparing(EntityReference::getName)); + // Note ordering is same as server side ordering by ID as string + // Patch requests work only if the same ordering of users on the server side + expectedTeams.sort(Comparator.comparing(entityReference -> entityReference.getId().toString())); + updatedTeams.sort(Comparator.comparing(entityReference -> entityReference.getId().toString())); updatedTeams.forEach(t -> t.setHref(null)); assertEquals(expectedTeams, updatedTeams); if (expected.getProfile() != null) {