Fix #5382 Backend: Update timestamp of thread for any reaction (#5443)

This commit is contained in:
Vivek Ratnavel Subramanian 2022-06-13 23:12:20 -07:00 committed by GitHub
parent ffcc88330c
commit d65c97c4e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 59 additions and 16 deletions

View File

@ -34,5 +34,5 @@ public class AuthConfiguration {
@Getter @Setter private CustomOIDCSSOClientConfig customOidc; @Getter @Setter private CustomOIDCSSOClientConfig customOidc;
@Getter @Setter private OpenMetadataJWTClientConfig openMetadataJWT; @Getter @Setter private OpenMetadataJWTClientConfig openmetadata;
} }

View File

@ -19,6 +19,7 @@ import static org.openmetadata.catalog.type.EventType.ENTITY_UPDATED;
import static org.openmetadata.common.utils.CommonUtil.listOrEmpty; import static org.openmetadata.common.utils.CommonUtil.listOrEmpty;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -233,6 +234,7 @@ public class ChangeEventHandler implements EventHandler {
.withThreadTs(System.currentTimeMillis()) .withThreadTs(System.currentTimeMillis())
.withCreatedBy(entityInterface.getUpdatedBy()) .withCreatedBy(entityInterface.getUpdatedBy())
.withAbout(about.getLinkString()) .withAbout(about.getLinkString())
.withReactions(Collections.emptyList())
.withUpdatedBy(entityInterface.getUpdatedBy()) .withUpdatedBy(entityInterface.getUpdatedBy())
.withUpdatedAt(System.currentTimeMillis()) .withUpdatedAt(System.currentTimeMillis())
.withMessage(message); .withMessage(message);
@ -265,6 +267,7 @@ public class ChangeEventHandler implements EventHandler {
.withThreadTs(System.currentTimeMillis()) .withThreadTs(System.currentTimeMillis())
.withCreatedBy(loggedInUserName) .withCreatedBy(loggedInUserName)
.withAbout(linkString) .withAbout(linkString)
.withReactions(Collections.emptyList())
.withUpdatedBy(loggedInUserName) .withUpdatedBy(loggedInUserName)
.withUpdatedAt(System.currentTimeMillis()) .withUpdatedAt(System.currentTimeMillis())
.withMessage(message); .withMessage(message);

View File

@ -382,8 +382,19 @@ public class FeedRepository {
return new ResultList<>(threads, beforeCursor, afterCursor, total); return new ResultList<>(threads, beforeCursor, afterCursor, total);
} }
private void storeReactions(Thread thread, String user) {
// Reactions are captured at the thread level. If the user reacted to a post of a thread,
// it will still be tracked as "user reacted to thread" since this will only be used to filter
// threads in the activity feed. Actual reactions are stored in thread.json or post.json itself.
// Multiple reactions by the same user on same thread or post is handled by
// field relationship table constraint (primary key)
dao.fieldRelationshipDAO()
.insert(user, thread.getId().toString(), Entity.USER, Entity.THREAD, Relationship.REACTED_TO.ordinal(), null);
}
@Transaction @Transaction
public final PatchResponse<Post> patchPost(Thread thread, Post post, JsonPatch patch) throws IOException { public final PatchResponse<Post> patchPost(Thread thread, Post post, String user, JsonPatch patch)
throws IOException {
// Apply JSON patch to the original post to get the updated post // Apply JSON patch to the original post to get the updated post
Post updated = JsonUtils.applyPatch(post, patch, Post.class); Post updated = JsonUtils.applyPatch(post, patch, Post.class);
@ -396,13 +407,24 @@ public class FeedRepository {
List<Post> posts = thread.getPosts(); List<Post> posts = thread.getPosts();
posts = posts.stream().filter(p -> !p.getId().equals(post.getId())).collect(Collectors.toList()); posts = posts.stream().filter(p -> !p.getId().equals(post.getId())).collect(Collectors.toList());
posts.add(updated); posts.add(updated);
thread.setPosts(posts); thread.withPosts(posts).withUpdatedAt(System.currentTimeMillis()).withUpdatedBy(user);
if (!updated.getReactions().isEmpty()) {
updated
.getReactions()
.forEach(
reaction -> {
storeReactions(thread, reaction.getUser().getName());
});
}
String change = patchUpdate(thread, post, updated) ? RestUtil.ENTITY_UPDATED : RestUtil.ENTITY_NO_CHANGE; String change = patchUpdate(thread, post, updated) ? RestUtil.ENTITY_UPDATED : RestUtil.ENTITY_NO_CHANGE;
return new PatchResponse<>(Status.OK, updated, change); return new PatchResponse<>(Status.OK, updated, change);
} }
@Transaction @Transaction
public final PatchResponse<Thread> patch(UriInfo uriInfo, UUID id, String user, JsonPatch patch) throws IOException { public final PatchResponse<Thread> patchThread(UriInfo uriInfo, UUID id, String user, JsonPatch patch)
throws IOException {
// Get all the fields in the original thread that can be updated during PATCH operation // Get all the fields in the original thread that can be updated during PATCH operation
Thread original = get(id.toString()); Thread original = get(id.toString());
@ -413,6 +435,15 @@ public class FeedRepository {
restorePatchAttributes(original, updated); restorePatchAttributes(original, updated);
if (!updated.getReactions().isEmpty()) {
updated
.getReactions()
.forEach(
reaction -> {
storeReactions(updated, reaction.getUser().getName());
});
}
// Update the attributes // Update the attributes
String change = patchUpdate(original, updated) ? RestUtil.ENTITY_UPDATED : RestUtil.ENTITY_NO_CHANGE; String change = patchUpdate(original, updated) ? RestUtil.ENTITY_UPDATED : RestUtil.ENTITY_NO_CHANGE;
Thread updatedHref = FeedResource.addHref(uriInfo, updated); Thread updatedHref = FeedResource.addHref(uriInfo, updated);

View File

@ -225,7 +225,7 @@ public class FeedResource {
JsonPatch patch) JsonPatch patch)
throws IOException { throws IOException {
PatchResponse<Thread> response = PatchResponse<Thread> response =
dao.patch(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch); dao.patchThread(uriInfo, UUID.fromString(id), securityContext.getUserPrincipal().getName(), patch);
return response.toResponse(); return response.toResponse();
} }
@ -333,7 +333,7 @@ public class FeedResource {
Thread thread = dao.get(threadId); Thread thread = dao.get(threadId);
Post post = dao.getPostById(thread, postId); Post post = dao.getPostById(thread, postId);
PatchResponse<Post> response = dao.patchPost(thread, post, patch); PatchResponse<Post> response = dao.patchPost(thread, post, securityContext.getUserPrincipal().getName(), patch);
return response.toResponse(); return response.toResponse();
} }

View File

@ -85,7 +85,7 @@ public final class OpenMetadataClientSecurityUtil {
openMetadataServerConnection.setSecurityConfig(customOIDCSSOClientConfig); openMetadataServerConnection.setSecurityConfig(customOIDCSSOClientConfig);
break; break;
case OPENMETADATA: case OPENMETADATA:
OpenMetadataJWTClientConfig openMetadataJWTClientConfig = authConfig.getOpenMetadataJWT(); OpenMetadataJWTClientConfig openMetadataJWTClientConfig = authConfig.getOpenmetadata();
checkAuthConfig(openMetadataJWTClientConfig, authProvider); checkAuthConfig(openMetadataJWTClientConfig, authProvider);
checkRequiredField(JWT_TOKEN, openMetadataJWTClientConfig.getJwtToken(), authProvider); checkRequiredField(JWT_TOKEN, openMetadataJWTClientConfig.getJwtToken(), authProvider);
openMetadataServerConnection.setSecurityConfig(openMetadataJWTClientConfig); openMetadataServerConnection.setSecurityConfig(openMetadataJWTClientConfig);

View File

@ -28,7 +28,8 @@
"upstream", "upstream",
"appliedTo", "appliedTo",
"relatedTo", "relatedTo",
"reviews" "reviews",
"reactedTo"
], ],
"javaEnums": [ "javaEnums": [
{ "name": "CONTAINS" }, { "name": "CONTAINS" },

View File

@ -33,7 +33,8 @@ class EnumBackwardCompatibilityTest {
/** */ /** */
@Test @Test
void testRelationshipEnumBackwardCompatible() { void testRelationshipEnumBackwardCompatible() {
assertEquals(17, Relationship.values().length); assertEquals(18, Relationship.values().length);
assertEquals(17, Relationship.REACTED_TO.ordinal());
assertEquals(16, Relationship.REVIEWS.ordinal()); assertEquals(16, Relationship.REVIEWS.ordinal());
} }

View File

@ -32,6 +32,7 @@ import static org.openmetadata.catalog.util.TestUtils.ADMIN_AUTH_HEADERS;
import static org.openmetadata.catalog.util.TestUtils.ADMIN_USER_NAME; import static org.openmetadata.catalog.util.TestUtils.ADMIN_USER_NAME;
import static org.openmetadata.catalog.util.TestUtils.NON_EXISTENT_ENTITY; import static org.openmetadata.catalog.util.TestUtils.NON_EXISTENT_ENTITY;
import static org.openmetadata.catalog.util.TestUtils.TEST_AUTH_HEADERS; import static org.openmetadata.catalog.util.TestUtils.TEST_AUTH_HEADERS;
import static org.openmetadata.catalog.util.TestUtils.TEST_USER_NAME;
import static org.openmetadata.catalog.util.TestUtils.assertListNotNull; import static org.openmetadata.catalog.util.TestUtils.assertListNotNull;
import static org.openmetadata.catalog.util.TestUtils.assertResponse; import static org.openmetadata.catalog.util.TestUtils.assertResponse;
import static org.openmetadata.catalog.util.TestUtils.assertResponseContains; import static org.openmetadata.catalog.util.TestUtils.assertResponseContains;
@ -425,10 +426,12 @@ public class FeedResourceTest extends CatalogApplicationTest {
String originalJson = JsonUtils.pojoToJson(thread); String originalJson = JsonUtils.pojoToJson(thread);
// add reactions // add reactions
Reaction reaction = new Reaction().withReactionType(ReactionType.HOORAY).withUser(USER.getEntityReference()); Reaction reaction = new Reaction().withReactionType(ReactionType.HOORAY).withUser(USER2.getEntityReference());
Thread updated = thread.withReactions(List.of(reaction)); Thread updated = thread.withReactions(List.of(reaction));
patchThreadAndCheck(updated, originalJson, ADMIN_AUTH_HEADERS); Thread patched = patchThreadAndCheck(updated, originalJson, TEST_AUTH_HEADERS);
assertNotEquals(patched.getUpdatedAt(), thread.getUpdatedAt());
assertEquals(TEST_USER_NAME, patched.getUpdatedBy());
} }
@Test @Test
@ -668,10 +671,13 @@ public class FeedResourceTest extends CatalogApplicationTest {
Post post = thread.getPosts().get(0); Post post = thread.getPosts().get(0);
String originalJson = JsonUtils.pojoToJson(post); String originalJson = JsonUtils.pojoToJson(post);
Reaction reaction1 = new Reaction().withReactionType(ReactionType.ROCKET).withUser(USER2.getEntityReference()); Reaction reaction1 = new Reaction().withReactionType(ReactionType.ROCKET).withUser(USER2.getEntityReference());
Reaction reaction2 = new Reaction().withReactionType(ReactionType.HOORAY).withUser(USER.getEntityReference()); Reaction reaction2 = new Reaction().withReactionType(ReactionType.HOORAY).withUser(USER2.getEntityReference());
post.withReactions(List.of(reaction1, reaction2)); post.withReactions(List.of(reaction1, reaction2));
Post updatedPost = patchPostAndCheck(thread.getId(), post, originalJson, AUTH_HEADERS); Post updatedPost = patchPostAndCheck(thread.getId(), post, originalJson, TEST_AUTH_HEADERS);
assertTrue(containsAll(updatedPost.getReactions(), List.of(reaction1, reaction2), REACTION_COMPARATOR)); assertTrue(containsAll(updatedPost.getReactions(), List.of(reaction1, reaction2), REACTION_COMPARATOR));
ThreadList threads = listThreads(null, 5, AUTH_HEADERS);
thread = threads.getData().get(0);
assertEquals(TEST_USER_NAME, thread.getUpdatedBy());
} }
@Test @Test

View File

@ -172,7 +172,7 @@ airflowConfiguration:
username: ${AIRFLOW_USERNAME:-admin} username: ${AIRFLOW_USERNAME:-admin}
password: ${AIRFLOW_PASSWORD:-admin} password: ${AIRFLOW_PASSWORD:-admin}
metadataApiEndpoint: ${SERVER_HOST_API_URL:-http://localhost:8585/api} metadataApiEndpoint: ${SERVER_HOST_API_URL:-http://localhost:8585/api}
authProvider: ${AIRFLOW_AUTH_PROVIDER:-"no-auth"} # Possible values are "no-auth", "azure", "google", "okta", "auth0", "customOidc" authProvider: ${AIRFLOW_AUTH_PROVIDER:-"no-auth"} # Possible values are "no-auth", "azure", "google", "okta", "auth0", "customOidc", "openmetadata"
authConfig: authConfig:
azure: azure:
clientSecret: ${OM_AUTH_AIRFLOW_AZURE_CLIENT_SECRET:-""} clientSecret: ${OM_AUTH_AIRFLOW_AZURE_CLIENT_SECRET:-""}
@ -196,7 +196,7 @@ airflowConfiguration:
clientId: ${OM_AUTH_AIRFLOW_CUSTOM_OIDC_CLIENT_ID:-""} clientId: ${OM_AUTH_AIRFLOW_CUSTOM_OIDC_CLIENT_ID:-""}
secretKey: ${OM_AUTH_AIRFLOW_CUSTOM_OIDC_SECRET_KEY_PATH:-""} secretKey: ${OM_AUTH_AIRFLOW_CUSTOM_OIDC_SECRET_KEY_PATH:-""}
tokenEndpoint: ${OM_AUTH_AIRFLOW_CUSTOM_OIDC_TOKEN_ENDPOINT_URL:-""} tokenEndpoint: ${OM_AUTH_AIRFLOW_CUSTOM_OIDC_TOKEN_ENDPOINT_URL:-""}
openMetadataJWT: openmetadata:
jwtToken: ${OM_AUTH_JWT_TOKEN:-""} jwtToken: ${OM_AUTH_JWT_TOKEN:-""}
slackEventPublishers: slackEventPublishers:

View File

@ -28,7 +28,8 @@
"upstream", "upstream",
"appliedTo", "appliedTo",
"relatedTo", "relatedTo",
"reviews" "reviews",
"reactedTo"
], ],
"javaEnums": [ "javaEnums": [
{ "name": "CONTAINS" }, { "name": "CONTAINS" },