mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2026-01-05 03:56:35 +00:00
This commit is contained in:
parent
58df9f110e
commit
b41b7d8bfb
@ -16,6 +16,7 @@ package org.openmetadata.catalog.jdbi3;
|
||||
import static org.openmetadata.catalog.Entity.helper;
|
||||
import static org.openmetadata.catalog.util.EntityUtil.toBoolean;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
@ -23,13 +24,18 @@ import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.json.JsonPatch;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jdbi.v3.sqlobject.transaction.Transaction;
|
||||
import org.openmetadata.catalog.Entity;
|
||||
import org.openmetadata.catalog.api.feed.EntityLinkThreadCount;
|
||||
import org.openmetadata.catalog.api.feed.ThreadCount;
|
||||
import org.openmetadata.catalog.entity.feed.Thread;
|
||||
import org.openmetadata.catalog.resources.feeds.FeedResource;
|
||||
import org.openmetadata.catalog.resources.feeds.FeedUtil;
|
||||
import org.openmetadata.catalog.resources.feeds.MessageParser;
|
||||
import org.openmetadata.catalog.resources.feeds.MessageParser.EntityLink;
|
||||
@ -39,6 +45,8 @@ import org.openmetadata.catalog.type.Post;
|
||||
import org.openmetadata.catalog.type.Relationship;
|
||||
import org.openmetadata.catalog.util.EntityUtil;
|
||||
import org.openmetadata.catalog.util.JsonUtils;
|
||||
import org.openmetadata.catalog.util.RestUtil;
|
||||
import org.openmetadata.catalog.util.RestUtil.PatchResponse;
|
||||
|
||||
public class FeedRepository {
|
||||
private final CollectionDAO dao;
|
||||
@ -230,6 +238,47 @@ public class FeedRepository {
|
||||
return limitPostsInThreads(threads, limitPosts);
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public final PatchResponse<Thread> patch(UriInfo uriInfo, UUID id, String user, JsonPatch patch)
|
||||
throws IOException, ParseException {
|
||||
// Get all the fields in the original thread that can be updated during PATCH operation
|
||||
Thread original = get(id.toString());
|
||||
|
||||
// Apply JSON patch to the original thread to get the updated thread
|
||||
Thread updated = JsonUtils.applyPatch(original, patch, Thread.class);
|
||||
// update the "updatedBy" and "updatedAt" fields
|
||||
updated.withUpdatedAt(System.currentTimeMillis()).withUpdatedBy(user);
|
||||
|
||||
restorePatchAttributes(original, updated);
|
||||
|
||||
// Update the attributes
|
||||
String change = patchUpdate(original, updated) ? RestUtil.ENTITY_UPDATED : RestUtil.ENTITY_NO_CHANGE;
|
||||
Thread updatedHref = FeedResource.addHref(uriInfo, updated);
|
||||
return new PatchResponse<>(Status.OK, updatedHref, change);
|
||||
}
|
||||
|
||||
private void restorePatchAttributes(Thread original, Thread updated) {
|
||||
// Patch can't make changes to following fields. Ignore the changes
|
||||
updated.withId(original.getId()).withAbout(original.getAbout());
|
||||
}
|
||||
|
||||
private boolean patchUpdate(Thread original, Thread updated) throws JsonProcessingException {
|
||||
updated.setId(original.getId());
|
||||
|
||||
// store the updated thread
|
||||
// if there is no change, there is no need to apply patch
|
||||
if (fieldsChanged(original, updated)) {
|
||||
dao.feedDAO().update(updated.getId().toString(), JsonUtils.pojoToJson(updated));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean fieldsChanged(Thread original, Thread updated) {
|
||||
// Patch supports only isResolved and message for now
|
||||
return original.getResolved() != updated.getResolved() || !original.getMessage().equals(updated.getMessage());
|
||||
}
|
||||
|
||||
private List<Thread> limitPostsInThreads(List<Thread> threads, int limitPosts) {
|
||||
for (Thread t : threads) {
|
||||
List<Post> posts = t.getPosts();
|
||||
|
||||
@ -13,25 +13,33 @@
|
||||
|
||||
package org.openmetadata.catalog.resources.feeds;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.v3.oas.annotations.ExternalDocumentation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import javax.json.JsonPatch;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PATCH;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
@ -51,6 +59,7 @@ import org.openmetadata.catalog.resources.Collection;
|
||||
import org.openmetadata.catalog.security.Authorizer;
|
||||
import org.openmetadata.catalog.type.Post;
|
||||
import org.openmetadata.catalog.util.RestUtil;
|
||||
import org.openmetadata.catalog.util.RestUtil.PatchResponse;
|
||||
import org.openmetadata.catalog.util.ResultList;
|
||||
|
||||
@Path("/v1/feed")
|
||||
@ -61,8 +70,15 @@ import org.openmetadata.catalog.util.ResultList;
|
||||
public class FeedResource {
|
||||
// TODO add /v1/feed?user=userid
|
||||
public static final String COLLECTION_PATH = "/v1/feed/";
|
||||
public static final List<String> ALLOWED_FIELDS = getAllowedFields();
|
||||
|
||||
private final FeedRepository dao;
|
||||
|
||||
private static List<String> getAllowedFields() {
|
||||
JsonPropertyOrder propertyOrder = Thread.class.getAnnotation(JsonPropertyOrder.class);
|
||||
return new ArrayList<>(Arrays.asList(propertyOrder.value()));
|
||||
}
|
||||
|
||||
public static List<Thread> addHref(UriInfo uriInfo, List<Thread> threads) {
|
||||
threads.forEach(t -> addHref(uriInfo, t));
|
||||
return threads;
|
||||
@ -148,6 +164,33 @@ public class FeedResource {
|
||||
return addHref(uriInfo, dao.get(id));
|
||||
}
|
||||
|
||||
@PATCH
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Update a thread by `id`.",
|
||||
tags = "feeds",
|
||||
description = "Update an existing thread using JsonPatch.",
|
||||
externalDocs = @ExternalDocumentation(description = "JsonPatch RFC", url = "https://tools.ietf.org/html/rfc6902"))
|
||||
@Consumes(MediaType.APPLICATION_JSON_PATCH_JSON)
|
||||
public Response updateThread(
|
||||
@Context UriInfo uriInfo,
|
||||
@Context SecurityContext securityContext,
|
||||
@PathParam("id") String id,
|
||||
@RequestBody(
|
||||
description = "JsonPatch with array of operations",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON_PATCH_JSON,
|
||||
examples = {
|
||||
@ExampleObject("[" + "{op:remove, path:/a}," + "{op:add, path: /b, value: val}" + "]")
|
||||
}))
|
||||
JsonPatch patch)
|
||||
throws IOException, ParseException {
|
||||
PatchResponse<Thread> response =
|
||||
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);
|
||||
return response.toResponse();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/count")
|
||||
@Operation(
|
||||
|
||||
@ -21,15 +21,18 @@ import static org.openmetadata.catalog.exception.CatalogExceptionMessage.entityN
|
||||
import static org.openmetadata.catalog.security.SecurityUtil.authHeaders;
|
||||
import static org.openmetadata.catalog.util.TestUtils.ADMIN_AUTH_HEADERS;
|
||||
import static org.openmetadata.catalog.util.TestUtils.NON_EXISTENT_ENTITY;
|
||||
import static org.openmetadata.catalog.util.TestUtils.assertListNotNull;
|
||||
import static org.openmetadata.catalog.util.TestUtils.assertResponse;
|
||||
import static org.openmetadata.catalog.util.TestUtils.assertResponseContains;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.json.JsonPatch;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -55,6 +58,7 @@ import org.openmetadata.catalog.resources.feeds.FeedResource.ThreadList;
|
||||
import org.openmetadata.catalog.type.Column;
|
||||
import org.openmetadata.catalog.type.ColumnDataType;
|
||||
import org.openmetadata.catalog.type.Post;
|
||||
import org.openmetadata.catalog.util.JsonUtils;
|
||||
import org.openmetadata.catalog.util.TestUtils;
|
||||
|
||||
@Slf4j
|
||||
@ -266,6 +270,38 @@ public class FeedResourceTest extends CatalogApplicationTest {
|
||||
assertEquals(POST_COUNT, postList.getData().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void patch_thread_200() throws IOException {
|
||||
// create a thread
|
||||
CreateThread create = create().withMessage("message");
|
||||
Thread thread = createAndCheck(create, ADMIN_AUTH_HEADERS);
|
||||
|
||||
String originalJson = JsonUtils.pojoToJson(thread);
|
||||
|
||||
// update message and resolved state
|
||||
Thread updated = thread.withMessage("updated message").withResolved(true);
|
||||
|
||||
patchThreadAndCheck(updated, originalJson, ADMIN_AUTH_HEADERS);
|
||||
}
|
||||
|
||||
@Test
|
||||
void patch_thread_not_allowed_fields() throws IOException {
|
||||
// create a thread
|
||||
CreateThread create = create().withMessage("message");
|
||||
Thread thread = createAndCheck(create, ADMIN_AUTH_HEADERS);
|
||||
|
||||
String originalJson = JsonUtils.pojoToJson(thread);
|
||||
|
||||
// update the About of the thread
|
||||
String originalAbout = thread.getAbout();
|
||||
Thread updated = thread.withAbout("<#E/user>");
|
||||
|
||||
patchThread(updated.getId(), originalJson, updated, ADMIN_AUTH_HEADERS);
|
||||
updated = getThread(thread.getId(), ADMIN_AUTH_HEADERS);
|
||||
// verify that the "About" is not changed
|
||||
validateThread(updated, thread.getMessage(), thread.getCreatedBy(), originalAbout);
|
||||
}
|
||||
|
||||
@Test
|
||||
void list_threadsWithPostsLimit() throws HttpResponseException {
|
||||
Thread thread = createAndCheck(create(), AUTH_HEADERS);
|
||||
@ -402,4 +438,33 @@ public class FeedResourceTest extends CatalogApplicationTest {
|
||||
linkThreadCount.stream().filter(l -> l.getEntityLink().equals(entityLink)).findFirst().orElseThrow();
|
||||
return threadCount.getCount();
|
||||
}
|
||||
|
||||
protected final Thread patchThreadAndCheck(Thread updated, String originalJson, Map<String, String> authHeaders)
|
||||
throws IOException {
|
||||
|
||||
// Validate information returned in patch response has the updates
|
||||
Thread returned = patchThread(updated.getId(), originalJson, updated, authHeaders);
|
||||
|
||||
compareEntities(updated, returned, authHeaders);
|
||||
|
||||
// GET the entity and Validate information returned
|
||||
Thread getEntity = getThread(updated.getId(), authHeaders);
|
||||
compareEntities(updated, getEntity, authHeaders);
|
||||
|
||||
return returned;
|
||||
}
|
||||
|
||||
public final Thread patchThread(UUID id, String originalJson, Thread updated, Map<String, String> authHeaders)
|
||||
throws JsonProcessingException, HttpResponseException {
|
||||
String updatedThreadJson = JsonUtils.pojoToJson(updated);
|
||||
JsonPatch patch = JsonUtils.getJsonPatch(originalJson, updatedThreadJson);
|
||||
return TestUtils.patch(getResource(String.format("feed/%s", id)), patch, Thread.class, authHeaders);
|
||||
}
|
||||
|
||||
public void compareEntities(Thread expected, Thread patched, Map<String, String> authHeaders) {
|
||||
assertListNotNull(patched.getId(), patched.getHref(), patched.getAbout());
|
||||
assertEquals(expected.getMessage(), patched.getMessage());
|
||||
assertEquals(expected.getResolved(), patched.getResolved());
|
||||
assertEquals(TestUtils.getPrincipal(authHeaders), patched.getUpdatedBy());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user