From fe67c4dca37b4292d716060b1283ed94a48f1373 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:16:02 -0500 Subject: [PATCH 1/4] UpdateOneCommand implementation. Also enable array and subdoc update. --- .../sgv3/docsapi/StargateDocsV3Api.java | 24 + .../docsapi/api/model/command/Command.java | 2 + .../UpdateClauseDeserializer.java | 3 +- .../model/command/impl/UpdateOneCommand.java | 19 + .../docsapi/api/v3/CollectionResource.java | 8 +- .../model/impl/UpdateOneCommandResolver.java | 44 ++ .../UpdateClauseDeserializerTest.java | 29 +- .../api/v3/FindAndUpdateIntegrationTest.java | 422 +++++++++++++++--- 8 files changed, 492 insertions(+), 59 deletions(-) create mode 100644 src/main/java/io/stargate/sgv3/docsapi/api/model/command/impl/UpdateOneCommand.java create mode 100644 src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneCommandResolver.java diff --git a/src/main/java/io/stargate/sgv3/docsapi/StargateDocsV3Api.java b/src/main/java/io/stargate/sgv3/docsapi/StargateDocsV3Api.java index 56711ee2a6..cf0257341f 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/StargateDocsV3Api.java +++ b/src/main/java/io/stargate/sgv3/docsapi/StargateDocsV3Api.java @@ -86,6 +86,18 @@ } } """), + @ExampleObject( + name = "updateOne", + summary = "`updateOne` command", + value = + """ + { + "updateOne": { + "filter": {"location": "London"}, + "update" : {"$set" : {"location" : "New York"}} + } + } + """), @ExampleObject( name = "deleteOne", summary = "`deleteOne` command", @@ -206,6 +218,18 @@ } } """), + @ExampleObject( + name = "resultUpdateOne", + summary = "`updateOne` command result", + value = + """ + { + "status": { + "updatedIds": ["1"] + } + } + } + """), @ExampleObject( name = "resultInsert", summary = "Insert command result", diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/Command.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/Command.java index fc3f57c079..f03b5f0432 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/Command.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/Command.java @@ -9,6 +9,7 @@ import io.stargate.sgv3.docsapi.api.model.command.impl.FindOneCommand; import io.stargate.sgv3.docsapi.api.model.command.impl.InsertManyCommand; import io.stargate.sgv3.docsapi.api.model.command.impl.InsertOneCommand; +import io.stargate.sgv3.docsapi.api.model.command.impl.UpdateOneCommand; /** * POJO object (data no behavior) that represents a syntactically and grammatically valid command as @@ -39,5 +40,6 @@ @JsonSubTypes.Type(value = FindOneAndUpdateCommand.class), @JsonSubTypes.Type(value = InsertOneCommand.class), @JsonSubTypes.Type(value = InsertManyCommand.class), + @JsonSubTypes.Type(value = UpdateOneCommand.class), }) public interface Command {} diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/UpdateClauseDeserializer.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/UpdateClauseDeserializer.java index 6ea406ce3b..9c334baa69 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/UpdateClauseDeserializer.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/UpdateClauseDeserializer.java @@ -62,7 +62,6 @@ private void getOperations( while (fields.hasNext()) { Map.Entry updateField = fields.next(); JsonNode value = updateField.getValue(); - if (!value.isValueNode()) throw new DocsException(ErrorCode.UNSUPPORTED_UPDATE_DATA_TYPE); expressionList.add( new UpdateOperation(updateField.getKey(), operator, jsonNodeValue(value))); } @@ -81,6 +80,8 @@ private static JsonNode jsonNodeValue(JsonNode node) { case NUMBER: case STRING: case NULL: + case ARRAY: + case OBJECT: return node; default: throw new DocsException( diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/model/command/impl/UpdateOneCommand.java b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/impl/UpdateOneCommand.java new file mode 100644 index 0000000000..2a0bd247b0 --- /dev/null +++ b/src/main/java/io/stargate/sgv3/docsapi/api/model/command/impl/UpdateOneCommand.java @@ -0,0 +1,19 @@ +package io.stargate.sgv3.docsapi.api.model.command.impl; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.stargate.sgv3.docsapi.api.model.command.Filterable; +import io.stargate.sgv3.docsapi.api.model.command.ReadCommand; +import io.stargate.sgv3.docsapi.api.model.command.clause.filter.FilterClause; +import io.stargate.sgv3.docsapi.api.model.command.clause.update.UpdateClause; +import javax.validation.Valid; +import org.eclipse.microprofile.openapi.annotations.media.Schema; + +@Schema( + description = + "Command that finds a single JSON document from a collection and updates the value provided in the update clause.") +@JsonTypeName("updateOne") +public record UpdateOneCommand( + @Valid @JsonProperty("filter") FilterClause filterClause, + @Valid @JsonProperty("update") UpdateClause updateClause) + implements ReadCommand, Filterable {} diff --git a/src/main/java/io/stargate/sgv3/docsapi/api/v3/CollectionResource.java b/src/main/java/io/stargate/sgv3/docsapi/api/v3/CollectionResource.java index 3e6f1cbd2f..ef5a89b210 100644 --- a/src/main/java/io/stargate/sgv3/docsapi/api/v3/CollectionResource.java +++ b/src/main/java/io/stargate/sgv3/docsapi/api/v3/CollectionResource.java @@ -10,6 +10,7 @@ import io.stargate.sgv3.docsapi.api.model.command.impl.FindOneCommand; import io.stargate.sgv3.docsapi.api.model.command.impl.InsertManyCommand; import io.stargate.sgv3.docsapi.api.model.command.impl.InsertOneCommand; +import io.stargate.sgv3.docsapi.api.model.command.impl.UpdateOneCommand; import io.stargate.sgv3.docsapi.config.constants.OpenApiConstants; import io.stargate.sgv3.docsapi.service.processor.CommandProcessor; import javax.inject.Inject; @@ -70,15 +71,17 @@ public CollectionResource(CommandProcessor commandProcessor) { FindCommand.class, FindOneAndUpdateCommand.class, InsertOneCommand.class, - InsertManyCommand.class + InsertManyCommand.class, + UpdateOneCommand.class }), examples = { + @ExampleObject(ref = "deleteOne"), @ExampleObject(ref = "findOne"), @ExampleObject(ref = "find"), @ExampleObject(ref = "findOneAndUpdate"), @ExampleObject(ref = "insertOne"), @ExampleObject(ref = "insertMany"), - @ExampleObject(ref = "deleteOne"), + @ExampleObject(ref = "updateOne"), })) @APIResponses( @APIResponse( @@ -95,6 +98,7 @@ public CollectionResource(CommandProcessor commandProcessor) { @ExampleObject(ref = "resultInsert"), @ExampleObject(ref = "resultError"), @ExampleObject(ref = "resultDelete"), + @ExampleObject(ref = "resultUpdateOne"), }))) @POST public Uni> postCommand( diff --git a/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneCommandResolver.java b/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneCommandResolver.java new file mode 100644 index 0000000000..0ae6ea1eb0 --- /dev/null +++ b/src/main/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneCommandResolver.java @@ -0,0 +1,44 @@ +package io.stargate.sgv3.docsapi.service.resolver.model.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.stargate.sgv3.docsapi.api.model.command.CommandContext; +import io.stargate.sgv3.docsapi.api.model.command.impl.UpdateOneCommand; +import io.stargate.sgv3.docsapi.service.operation.model.Operation; +import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; +import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadAndUpdateOperation; +import io.stargate.sgv3.docsapi.service.resolver.model.CommandResolver; +import io.stargate.sgv3.docsapi.service.resolver.model.impl.matcher.FilterableResolver; +import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; +import javax.enterprise.context.ApplicationScoped; +import javax.inject.Inject; + +/** Resolves the {@link UpdateOneCommand } */ +@ApplicationScoped +public class UpdateOneCommandResolver extends FilterableResolver + implements CommandResolver { + private Shredder shredder; + + @Inject + public UpdateOneCommandResolver(ObjectMapper objectMapper, Shredder shredder) { + super(objectMapper, true, true); + this.shredder = shredder; + } + + @Override + public Class getCommandClass() { + return UpdateOneCommand.class; + } + + @Override + public Operation resolveCommand(CommandContext ctx, UpdateOneCommand command) { + ReadOperation readOperation = resolve(ctx, command); + DocumentUpdater documentUpdater = new DocumentUpdater(command.updateClause()); + return new ReadAndUpdateOperation(ctx, readOperation, documentUpdater, false, shredder); + } + + @Override + protected FilteringOptions getFilteringOption(UpdateOneCommand command) { + return new FilteringOptions(1, null, 1); + } +} diff --git a/src/test/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/UpdateClauseDeserializerTest.java b/src/test/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/UpdateClauseDeserializerTest.java index ec8b4ed45a..6bed7c8d42 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/UpdateClauseDeserializerTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/api/model/command/deserializers/UpdateClauseDeserializerTest.java @@ -1,7 +1,6 @@ package io.stargate.sgv3.docsapi.api.model.command.deserializers; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.catchThrowable; import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.test.junit.QuarkusTest; @@ -10,7 +9,6 @@ import io.stargate.sgv3.docsapi.api.model.command.clause.update.UpdateClause; import io.stargate.sgv3.docsapi.api.model.command.clause.update.UpdateOperation; import io.stargate.sgv3.docsapi.api.model.command.clause.update.UpdateOperator; -import io.stargate.sgv3.docsapi.exception.DocsException; import javax.inject.Inject; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -95,13 +93,32 @@ public void mustHandleBoolean() throws Exception { } @Test - public void unsupportedFilterTypes() { + public void mustHandleArray() throws Exception { String json = """ - {"$set" : {"boolType": ["a"]}} + {"$set" : {"arrayType": ["a"]}} """; - Throwable throwable = catchThrowable(() -> objectMapper.readValue(json, UpdateClause.class)); - assertThat(throwable).isInstanceOf(DocsException.class); + final UpdateOperation operation = + new UpdateOperation( + "arrayType", UpdateOperator.SET, objectMapper.getNodeFactory().arrayNode(1).add("a")); + UpdateClause updateClause = objectMapper.readValue(json, UpdateClause.class); + assertThat(updateClause.updateOperations()).hasSize(1).contains(operation); + } + + @Test + public void mustHandleSubdoc() throws Exception { + String json = + """ + {"$set" : {"subDocType": {"sub_doc_col" : "sub_doc_val"}}}} + """; + + final UpdateOperation operation = + new UpdateOperation( + "subDocType", + UpdateOperator.SET, + objectMapper.getNodeFactory().objectNode().put("sub_doc_col", "sub_doc_val")); + UpdateClause updateClause = objectMapper.readValue(json, UpdateClause.class); + assertThat(updateClause.updateOperations()).hasSize(1).contains(operation); } } } diff --git a/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java b/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java index 5f5b2560d7..a8e24f76f5 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java @@ -28,16 +28,16 @@ class FindAndUpdate { public void findByIdAndSet() { String json = """ - { - "insertOne": { - "document": { - "_id": "doc3", - "username": "user3", - "active_user" : true - } - } - } - """; + { + "insertOne": { + "document": { + "_id": "doc3", + "username": "user3", + "active_user" : true + } + } + } + """; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -50,13 +50,13 @@ public void findByIdAndSet() { json = """ - { - "findOneAndUpdate": { - "filter" : {"_id" : "doc3"}, - "update" : {"$set" : {"active_user": false}} - } - } - """; + { + "findOneAndUpdate": { + "filter" : {"_id" : "doc3"}, + "update" : {"$set" : {"active_user": false}} + } + } + """; String expected = "{\"_id\":\"doc3\", \"username\":\"user3\", \"active_user\":true}"; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -94,15 +94,15 @@ public void findByIdAndSet() { public void findByColumnAndSet() { String json = """ - { - "insertOne": { - "document": { - "_id": "doc4", - "username": "user4" - } - } - } - """; + { + "insertOne": { + "document": { + "_id": "doc4", + "username": "user4" + } + } + } + """; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -114,13 +114,13 @@ public void findByColumnAndSet() { .statusCode(200); json = """ - { - "findOneAndUpdate": { - "filter" : {"username" : "user4"}, - "update" : {"$set" : {"new_col": "new_val"}} - } - } - """; + { + "findOneAndUpdate": { + "filter" : {"username" : "user4"}, + "update" : {"$set" : {"new_col": "new_val"}} + } + } + """; String expected = "{\"_id\":\"doc4\", \"username\":\"user4\"}"; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -158,16 +158,16 @@ public void findByColumnAndSet() { public void findByIdAndUnset() { String json = """ - { - "insertOne": { - "document": { - "_id": "doc5", - "username": "user5", - "unset_col": "val" - } - } - } - """; + { + "insertOne": { + "document": { + "_id": "doc5", + "username": "user5", + "unset_col": "val" + } + } + } + """; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -180,13 +180,13 @@ public void findByIdAndUnset() { json = """ - { - "findOneAndUpdate": { - "filter" : {"_id" : "doc5"}, - "update" : {"$unset" : {"unset_col": ""}} - } - } - """; + { + "findOneAndUpdate": { + "filter" : {"_id" : "doc5"}, + "update" : {"$unset" : {"unset_col": ""}} + } + } + """; String expected = "{\"_id\":\"doc5\", \"username\":\"user5\", \"unset_col\":\"val\"}"; given() .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) @@ -219,4 +219,326 @@ public void findByIdAndUnset() { .body("data.docs[0]", jsonEquals(expected)); } } + + @Nested + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) + class UpdateOne { + @Test + @Order(2) + public void findByIdAndSet() { + String json = + """ + { + "insertOne": { + "document": { + "_id": "update_doc1", + "username": "update_user3", + "active_user" : true + } + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200); + + json = + """ + { + "findOneAndUpdate": { + "filter" : {"_id" : "update_doc1"}, + "update" : {"$set" : {"active_user": false}} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.updatedIds[0]", is("update_doc1")); + + String expected = + "{\"_id\":\"update_doc1\", \"username\":\"update_user3\", \"active_user\":false}"; + json = + """ + { + "find": { + "filter" : {"_id" : "update_doc1"} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + + @Test + @Order(2) + public void findByColumnAndSet() { + String json = + """ + { + "insertOne": { + "document": { + "_id": "update_doc2", + "username": "update_user2" + } + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200); + json = + """ + { + "updateOne": { + "filter" : {"username" : "update_user2"}, + "update" : {"$set" : {"new_col": "new_val"}} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.updatedIds[0]", is("update_doc2")); + + String expected = + "{\"_id\":\"update_doc2\", \"username\":\"update_user2\", \"new_col\": \"new_val\"}"; + json = + """ + { + "find": { + "filter" : {"_id" : "update_doc2"} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + + @Test + @Order(2) + public void findByIdAndUnset() { + String json = + """ + { + "insertOne": { + "document": { + "_id": "update_doc2", + "username": "update_user3", + "unset_col": "val" + } + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200); + + json = + """ + { + "findOneAndUpdate": { + "filter" : {"_id" : "update_doc3"}, + "update" : {"$unset" : {"unset_col": ""}} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.updatedIds[0]", is("update_doc3")); + + String expected = "{\"_id\":\"update_doc3\", \"username\":\"update_user3\"}"; + json = + """ + { + "find": { + "filter" : {"_id" : "update_user3"} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + + @Test + @Order(2) + public void findByColumnAndSetArray() { + String json = + """ + { + "insertOne": { + "document": { + "_id": "update_doc4", + "username": "update_user4" + } + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200); + json = + """ + { + "updateOne": { + "filter" : {"username" : "update_user4"}, + "update" : {"$set" : {"new_col": ["new_val", "new_val2"]}} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.updatedIds[0]", is("update_user4")); + + String expected = + "{\"_id\":\"update_doc4\", \"username\":\"update_user4\", \"new_col\": [\"new_val\", \"new_val2\"]}"; + json = + """ + { + "find": { + "filter" : {"_id" : "update_doc4"} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + + @Test + @Order(2) + public void findByColumnAndSetSubDoc() { + String json = + """ + { + "insertOne": { + "document": { + "_id": "update_doc5", + "username": "update_user5" + } + } + } + """; + + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200); + json = + """ + { + "updateOne": { + "filter" : {"username" : "update_user5"}, + "update" : {"$set" : {"new_col": {"sub_doc_col" : "new_val2"}}} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("status.updatedIds[0]", is("update_user5")); + + String expected = + "{\"_id\":\"update_doc5\", \"username\":\"update_user5\", \"new_col\": {\"sub_doc_col\":\"new_val2\"}}"; + json = + """ + { + "find": { + "filter" : {"_id" : "update_doc5"} + } + } + """; + given() + .header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken()) + .contentType(ContentType.JSON) + .body(json) + .when() + .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) + .then() + .statusCode(200) + .body("data.docs[0]", jsonEquals(expected)); + } + } } From 6ed3f6c3fc83ae2fba88a34853824a07c4e3ff70 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:22:35 -0500 Subject: [PATCH 2/4] Added resolver test --- .../model/impl/UpdateOneResolverTest.java | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java diff --git a/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java new file mode 100644 index 0000000000..259e4d20fc --- /dev/null +++ b/src/test/java/io/stargate/sgv3/docsapi/service/resolver/model/impl/UpdateOneResolverTest.java @@ -0,0 +1,127 @@ +package io.stargate.sgv3.docsapi.service.resolver.model.impl; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; +import io.stargate.sgv2.common.testprofiles.NoGlobalResourcesTestProfile; +import io.stargate.sgv3.docsapi.api.model.command.CommandContext; +import io.stargate.sgv3.docsapi.api.model.command.clause.update.UpdateClause; +import io.stargate.sgv3.docsapi.api.model.command.clause.update.UpdateOperation; +import io.stargate.sgv3.docsapi.api.model.command.clause.update.UpdateOperator; +import io.stargate.sgv3.docsapi.api.model.command.impl.UpdateOneCommand; +import io.stargate.sgv3.docsapi.service.operation.model.Operation; +import io.stargate.sgv3.docsapi.service.operation.model.ReadOperation; +import io.stargate.sgv3.docsapi.service.operation.model.impl.FindOperation; +import io.stargate.sgv3.docsapi.service.operation.model.impl.ReadAndUpdateOperation; +import io.stargate.sgv3.docsapi.service.shredding.Shredder; +import io.stargate.sgv3.docsapi.service.updater.DocumentUpdater; +import java.util.List; +import javax.inject.Inject; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +@QuarkusTest +@TestProfile(NoGlobalResourcesTestProfile.Impl.class) +public class UpdateOneResolverTest { + @Inject ObjectMapper objectMapper; + @Inject Shredder shredder; + @Inject UpdateOneCommandResolver updateOneCommandResolver; + + @Nested + class UpdateOneCommandResolveCommand { + + @Test + public void idFilterConditionBsonType() throws Exception { + String json = + """ + { + "updateOne": { + "filter" : {"_id" : "id"}, + "update" : {"$set" : {"location" : "New York"}} + } + } + """; + + UpdateOneCommand updateOneCommand = objectMapper.readValue(json, UpdateOneCommand.class); + final CommandContext commandContext = new CommandContext("database", "collection"); + final Operation operation = + updateOneCommandResolver.resolveCommand(commandContext, updateOneCommand); + ReadOperation readOperation = + new FindOperation( + commandContext, + List.of(new FindOperation.IDFilter(FindOperation.IDFilter.Operator.EQ, "id")), + null, + 1, + 1, + true, + objectMapper); + + DocumentUpdater documentUpdater = + new DocumentUpdater( + new UpdateClause( + List.of( + new UpdateOperation( + "location", + UpdateOperator.SET, + objectMapper.getNodeFactory().textNode("New York"))))); + ReadAndUpdateOperation expected = + new ReadAndUpdateOperation( + commandContext, readOperation, documentUpdater, false, shredder); + assertThat(operation) + .isInstanceOf(ReadAndUpdateOperation.class) + .satisfies( + op -> { + assertThat(op).isEqualTo(expected); + }); + } + + @Test + public void dynamicFilterCondition() throws Exception { + String json = + """ + { + "updateOne": { + "filter" : {"col" : "val"}, + "update" : {"$set" : {"location" : "New York"}} + } + } + """; + + UpdateOneCommand updateOneCommand = objectMapper.readValue(json, UpdateOneCommand.class); + final CommandContext commandContext = new CommandContext("database", "collection"); + final Operation operation = + updateOneCommandResolver.resolveCommand(commandContext, updateOneCommand); + ReadOperation readOperation = + new FindOperation( + commandContext, + List.of( + new FindOperation.TextFilter( + "col", FindOperation.MapFilterBase.Operator.EQ, "val")), + null, + 1, + 1, + true, + objectMapper); + + DocumentUpdater documentUpdater = + new DocumentUpdater( + new UpdateClause( + List.of( + new UpdateOperation( + "location", + UpdateOperator.SET, + objectMapper.getNodeFactory().textNode("New York"))))); + ReadAndUpdateOperation expected = + new ReadAndUpdateOperation( + commandContext, readOperation, documentUpdater, false, shredder); + assertThat(operation) + .isInstanceOf(ReadAndUpdateOperation.class) + .satisfies( + op -> { + assertThat(op).isEqualTo(expected); + }); + } + } +} From e9229ccefce8a30b1b422db22a36ca052eb299ab Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:30:35 -0500 Subject: [PATCH 3/4] IT cases fix --- .../sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java b/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java index a8e24f76f5..4660fe46b2 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java @@ -359,7 +359,7 @@ public void findByIdAndUnset() { { "insertOne": { "document": { - "_id": "update_doc2", + "_id": "update_doc3", "username": "update_user3", "unset_col": "val" } @@ -455,7 +455,7 @@ public void findByColumnAndSetArray() { .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() .statusCode(200) - .body("status.updatedIds[0]", is("update_user4")); + .body("status.updatedIds[0]", is("update_doc4")); String expected = "{\"_id\":\"update_doc4\", \"username\":\"update_user4\", \"new_col\": [\"new_val\", \"new_val2\"]}"; @@ -518,7 +518,7 @@ public void findByColumnAndSetSubDoc() { .post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName) .then() .statusCode(200) - .body("status.updatedIds[0]", is("update_user5")); + .body("status.updatedIds[0]", is("update_doc5")); String expected = "{\"_id\":\"update_doc5\", \"username\":\"update_user5\", \"new_col\": {\"sub_doc_col\":\"new_val2\"}}"; From 8f3e79d5b819d0d2a6aeaa06fd0c902962268086 Mon Sep 17 00:00:00 2001 From: maheshrajamani <99678631+maheshrajamani@users.noreply.github.com> Date: Thu, 26 Jan 2023 11:51:22 -0500 Subject: [PATCH 4/4] IT cases fix --- .../sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java b/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java index 4660fe46b2..acf4a0e030 100644 --- a/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java +++ b/src/test/java/io/stargate/sgv3/docsapi/api/v3/FindAndUpdateIntegrationTest.java @@ -400,7 +400,7 @@ public void findByIdAndUnset() { """ { "find": { - "filter" : {"_id" : "update_user3"} + "filter" : {"_id" : "update_doc3"} } } """;