From 5d0e403611d10a03cedcd47296fcb0eaef2d799e Mon Sep 17 00:00:00 2001 From: Yannan <73408381+YannanGao-gs@users.noreply.github.com> Date: Fri, 8 Mar 2024 19:10:52 -0500 Subject: [PATCH] introduce a new API to update provided query fields and update query protocol to record originalVersionId (#2633) --- .../query/api/ApplicationQuery.java | 22 ++++++++++ .../query/api/QueryStoreManager.java | 40 ++++++++++++++++++- .../engine/application/query/model/Query.java | 1 + .../query/api/TestQueryStoreManager.java | 19 +++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/api/ApplicationQuery.java b/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/api/ApplicationQuery.java index cd1895fee63..fd5138c023c 100644 --- a/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/api/ApplicationQuery.java +++ b/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/api/ApplicationQuery.java @@ -187,6 +187,28 @@ public Response updateQuery(@PathParam("queryId") String queryId, Query query, @ } } + @PUT + @Path("{queryId}/patchQuery") + @ApiOperation(value = "Patch Query - update selected query fields") + @Consumes({MediaType.APPLICATION_JSON}) + public Response patchQuery(@PathParam("queryId") String queryId, Query query, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager profileManager) + { + MutableList profiles = ProfileManagerHelper.extractProfiles(profileManager); + Identity identity = IdentityFactoryProvider.getInstance().makeIdentity(profiles); + try (Scope scope = GlobalTracer.get().buildSpan("Patch Query - update selected query fields").startActive(true)) + { + return Response.ok().entity(this.queryStoreManager.patchQuery(queryId, query, getCurrentUser(profileManager))).build(); + } + catch (Exception e) + { + if (e instanceof ApplicationQueryException) + { + return ((ApplicationQueryException) e).toResponse(); + } + return ExceptionTool.exceptionManager(e, LoggingEventType.UPDATE_QUERY_ERROR, identity.getName()); + } + } + @DELETE @Path("{queryId}") @ApiOperation(value = "Delete the query with specified ID") diff --git a/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/api/QueryStoreManager.java b/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/api/QueryStoreManager.java index 87c95568d49..1884b651192 100644 --- a/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/api/QueryStoreManager.java +++ b/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/api/QueryStoreManager.java @@ -65,7 +65,7 @@ public class QueryStoreManager // so that it records the dataSpace it is created from private static final String QUERY_PROFILE_PATH = "meta::pure::profiles::query"; private static final String QUERY_PROFILE_TAG_DATA_SPACE = "dataSpace"; - private static final List LIGHT_QUERY_PROJECTION = Arrays.asList("id", "name", "versionId", "groupId", "artifactId", "owner", "createdAt", "lastUpdatedAt"); + private static final List LIGHT_QUERY_PROJECTION = Arrays.asList("id", "name", "versionId", "originalVersionId", "groupId", "artifactId", "owner", "createdAt", "lastUpdatedAt"); private static final int GET_QUERIES_LIMIT = 50; private final MongoClient mongoClient; @@ -110,6 +110,7 @@ private static Query documentToQuery(Document document) query.groupId = document.getString("groupId"); query.artifactId = document.getString("artifactId"); query.versionId = document.getString("versionId"); + query.originalVersionId = document.getString("originalVersionId"); query.mapping = document.getString("mapping"); query.runtime = document.getString("runtime"); query.content = document.getString("content"); @@ -386,6 +387,7 @@ else if (matchingQueries.size() == 0) query.owner = currentUser; query.createdAt = currentQuery.createdAt; query.lastUpdatedAt = Instant.now().toEpochMilli(); + query.originalVersionId = currentQuery.originalVersionId; this.getQueryCollection().findOneAndReplace(Filters.eq("id", queryId), queryToDocument(query)); QueryEvent updatedEvent = createEvent(query.id, QueryEvent.QueryEventType.UPDATED); updatedEvent.timestamp = query.lastUpdatedAt; @@ -393,6 +395,42 @@ else if (matchingQueries.size() == 0) return query; } + public Query patchQuery(String queryId, Query updatedQuery, String currentUser) throws JsonProcessingException + { + Query currentQuery = this.getQuery(queryId); + // Make sure only the owner can update the query + // NOTE: if the query is created by an anonymous user previously, set the current user as the owner + if (currentQuery.owner != null && !currentQuery.owner.equals(currentUser)) + { + throw new ApplicationQueryException("Only owner can update the query", Response.Status.FORBIDDEN); + } + + Class queryClass = currentQuery.getClass(); + for (java.lang.reflect.Field field : queryClass.getDeclaredFields()) + { + try + { + field.setAccessible(true); + Object updatedValue = field.get(updatedQuery); + if (updatedValue != null) + { + field.set(currentQuery, updatedValue); + } + } + catch (IllegalAccessException e) + { + throw new ApplicationQueryException("Can't modify query field" + field.getName(), Response.Status.BAD_REQUEST); + } + } + currentQuery.owner = currentUser; + currentQuery.lastUpdatedAt = Instant.now().toEpochMilli(); + this.getQueryCollection().findOneAndReplace(Filters.eq("id", queryId), queryToDocument(currentQuery)); + QueryEvent updatedEvent = createEvent(queryId, QueryEvent.QueryEventType.UPDATED); + updatedEvent.timestamp = currentQuery.lastUpdatedAt; + this.getQueryEventCollection().insertOne(queryEventToDocument(updatedEvent)); + return currentQuery; + } + public void deleteQuery(String queryId, String currentUser) throws JsonProcessingException { List matchingQueries = LazyIterate.collect(this.getQueryCollection().find(Filters.eq("id", queryId)), QueryStoreManager::documentToQuery).toList(); diff --git a/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/model/Query.java b/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/model/Query.java index 28ce2030a3f..20107b32c3c 100644 --- a/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/model/Query.java +++ b/legend-engine-application-query/src/main/java/org/finos/legend/engine/application/query/model/Query.java @@ -27,6 +27,7 @@ public class Query public String groupId; public String artifactId; public String versionId; + public String originalVersionId; public String mapping; public String runtime; public String content; diff --git a/legend-engine-application-query/src/test/java/org/finos/legend/engine/application/query/api/TestQueryStoreManager.java b/legend-engine-application-query/src/test/java/org/finos/legend/engine/application/query/api/TestQueryStoreManager.java index 58ef9b882b4..f5a81883028 100644 --- a/legend-engine-application-query/src/test/java/org/finos/legend/engine/application/query/api/TestQueryStoreManager.java +++ b/legend-engine-application-query/src/test/java/org/finos/legend/engine/application/query/api/TestQueryStoreManager.java @@ -125,6 +125,7 @@ static class TestQueryBuilder public String groupId = "test.group"; public String artifactId = "test-artifact"; public String versionId = "0.0.0"; + public String originalVersionId = "0.0.0"; public String description = "description"; public String mapping = "mapping"; public String runtime = "runtime"; @@ -187,6 +188,7 @@ Query build() query.groupId = this.groupId; query.artifactId = this.artifactId; query.versionId = this.versionId; + query.originalVersionId = this.originalVersionId; query.mapping = this.mapping; query.runtime = this.runtime; query.content = this.content; @@ -353,6 +355,7 @@ public void testSearchQueries() throws Exception Assert.assertEquals("1", lightQuery.id); Assert.assertEquals("query1", lightQuery.name); Assert.assertEquals("0.0.0", lightQuery.versionId); + Assert.assertEquals("0.0.0", lightQuery.originalVersionId); Assert.assertNotNull(lightQuery.createdAt); Assert.assertNotNull(lightQuery.lastUpdatedAt); Assert.assertNull(lightQuery.content); @@ -566,6 +569,7 @@ public void testCreateSimpleQuery() throws Exception Assert.assertEquals("1", createdQuery.id); Assert.assertEquals("query1", createdQuery.name); Assert.assertEquals("0.0.0", createdQuery.versionId); + Assert.assertEquals("0.0.0", createdQuery.originalVersionId); Assert.assertEquals("content", createdQuery.content); Assert.assertEquals("description", createdQuery.description); Assert.assertEquals("mapping", createdQuery.mapping); @@ -613,6 +617,21 @@ public void testUpdateQuery() throws Exception Assert.assertEquals("query2", queryStoreManager.getQuery("1").name); } + @Test + public void testUpdateQueryVersion() throws Exception + { + String currentUser = "testUser"; + queryStoreManager.createQuery(TestQueryBuilder.create("1", "query1", currentUser).build(), currentUser); + queryStoreManager.updateQuery("1", TestQueryBuilder.create("1", "query2", currentUser).build(), currentUser); + Assert.assertEquals("query2", queryStoreManager.getQuery("1").name); + Query queryWithSelectedFields = new Query(); + queryWithSelectedFields.id = "1"; + queryWithSelectedFields.versionId = "1.0.0"; + queryStoreManager.patchQuery("1", queryWithSelectedFields, currentUser); + Assert.assertEquals("1.0.0", queryStoreManager.getQuery("1").versionId); + Assert.assertEquals("0.0.0", queryStoreManager.getQuery("1").originalVersionId); + } + @Test public void testUpdateWithInvalidQuery() {