From b03f3da6171bb1489280e98ce34f908d3baf8722 Mon Sep 17 00:00:00 2001 From: Aayush Kataria Date: Thu, 11 Nov 2021 19:25:22 -0800 Subject: [PATCH] Patch Api for Encryption (#25195) * Patch Api for Encryption * Patch Api for Encryption * Patch Api for Encryption * Patch Api for Encryption * Encryption Patch * Encryption Patch * Encryption Patch * Encryption Patch * Encryption Patch Co-authored-by: Aayush Kataria --- .../CosmosEncryptionAsyncContainer.java | 106 ++++++++++++++++ .../implementation/EncryptionProcessor.java | 118 ++++++++++++++++-- .../EncryptionAsyncApiCrudTest.java | 89 ++++++++++++- .../cosmos/encryption/EncryptionPojo.java | 6 +- .../implementation/patch/PatchOperation.java | 2 +- .../patch/PatchOperationCore.java | 4 +- 6 files changed, 309 insertions(+), 16 deletions(-) diff --git a/sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/CosmosEncryptionAsyncContainer.java b/sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/CosmosEncryptionAsyncContainer.java index e0c2eef64b534..522757d7a13b1 100644 --- a/sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/CosmosEncryptionAsyncContainer.java +++ b/sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/CosmosEncryptionAsyncContainer.java @@ -34,6 +34,11 @@ import com.azure.cosmos.models.PartitionKey; import com.azure.cosmos.models.SqlParameter; import com.azure.cosmos.models.SqlQuerySpec; +import com.azure.cosmos.models.CosmosPatchOperations; +import com.azure.cosmos.implementation.patch.PatchOperation; +import com.azure.cosmos.implementation.patch.PatchOperationCore; +import com.azure.cosmos.implementation.patch.PatchOperationType; +import com.azure.cosmos.models.CosmosPatchItemRequestOptions; import com.azure.cosmos.util.CosmosPagedFlux; import com.azure.cosmos.util.UtilBridgeInternal; import com.fasterxml.jackson.databind.JsonNode; @@ -72,6 +77,7 @@ public class CosmosEncryptionAsyncContainer { ImplementationBridgeHelpers.CosmosBatchResponseHelper.CosmosBatchResponseAccessor cosmosBatchResponseAccessor; ImplementationBridgeHelpers.CosmosBatchOperationResultHelper.CosmosBatchOperationResultAccessor cosmosBatchOperationResultAccessor; ImplementationBridgeHelpers.CosmosBatchRequestOptionsHelper.CosmosBatchRequestOptionsAccessor cosmosBatchRequestOptionsAccessor; + ImplementationBridgeHelpers.CosmosPatchOperationsHelper.CosmosPatchOperationsAccessor cosmosPatchOperationsAccessor; CosmosEncryptionAsyncContainer(CosmosAsyncContainer container, CosmosEncryptionAsyncClient cosmosEncryptionAsyncClient) { @@ -93,6 +99,7 @@ public class CosmosEncryptionAsyncContainer { this.cosmosBatchResponseAccessor = ImplementationBridgeHelpers.CosmosBatchResponseHelper.getCosmosBatchResponseAccessor(); this.cosmosBatchOperationResultAccessor = ImplementationBridgeHelpers.CosmosBatchOperationResultHelper.getCosmosBatchOperationResultAccessor(); this.cosmosBatchRequestOptionsAccessor = ImplementationBridgeHelpers.CosmosBatchRequestOptionsHelper.getCosmosBatchRequestOptionsAccessor(); + this.cosmosPatchOperationsAccessor = ImplementationBridgeHelpers.CosmosPatchOperationsHelper.getCosmosPatchOperationsAccessor(); } EncryptionProcessor getEncryptionProcessor() { @@ -530,6 +537,105 @@ public CosmosPagedFlux queryChangeFeed(CosmosChangeFeedRequestOptions opt return queryChangeFeedHelper(options, classType,false); } + /** + * Run patch operations on an Item. + *

+ * After subscription the operation will be performed. + * The {@link Mono} upon successful completion will contain a single Cosmos item response with the patched item. + * + * @param the type parameter. + * @param itemId the item id. + * @param partitionKey the partition key. + * @param cosmosPatchOperations Represents a container having list of operations to be sequentially applied to the referred Cosmos item. + * @param options the request options. + * @param itemType the item type. + * + * @return an {@link Mono} containing the Cosmos item resource response with the patched item or an error. + */ + public Mono> patchItem( + String itemId, + PartitionKey partitionKey, + CosmosPatchOperations cosmosPatchOperations, + CosmosPatchItemRequestOptions options, + Class itemType) { + + checkNotNull(itemId, "expected non-null itemId"); + checkNotNull(partitionKey, "expected non-null partitionKey for patchItem"); + checkNotNull(cosmosPatchOperations, "expected non-null cosmosPatchOperations"); + + if (options == null) { + options = new CosmosPatchItemRequestOptions(); + } + + return patchItemHelper(itemId, partitionKey, cosmosPatchOperations, options, itemType); + } + + private Mono> patchItemHelper(String itemId, + PartitionKey partitionKey, + CosmosPatchOperations cosmosPatchOperations, + CosmosPatchItemRequestOptions options, + Class itemType) { + this.setRequestHeaders(options); + List> monoList = new ArrayList<>(); + for (PatchOperation patchOperation : this.cosmosPatchOperationsAccessor.getPatchOperations(cosmosPatchOperations)) { + Mono itemPatchOperationMono = null; + if (patchOperation.getOperationType() == PatchOperationType.REMOVE) { + itemPatchOperationMono = Mono.just(patchOperation); + } + else if (patchOperation.getOperationType() == PatchOperationType.INCREMENT) { + throw new IllegalArgumentException("Increment patch operation is not allowed for encrypted path"); + } + else if (patchOperation instanceof PatchOperationCore) { + JsonNode objectNode = EncryptionUtils.getSimpleObjectMapper().valueToTree(((PatchOperationCore)patchOperation).getResource()); + itemPatchOperationMono = + encryptionProcessor.encryptPatchNode(objectNode, ((PatchOperationCore)patchOperation).getPath()).map(encryptedObjectNode -> { + return new PatchOperationCore<>( + patchOperation.getOperationType(), + ((PatchOperationCore)patchOperation).getPath(), + encryptedObjectNode + ); + }); + } + monoList.add(itemPatchOperationMono); + } + Mono> encryptedPatchOperationsListMono = + Flux.mergeSequential(monoList).collectList(); + CosmosPatchItemRequestOptions finalRequestOptions = options; + + CosmosPatchOperations encryptedCosmosPatchOperations = CosmosPatchOperations.create(); + + return encryptedPatchOperationsListMono.flatMap(patchOperations -> { + this.cosmosPatchOperationsAccessor.getPatchOperations(encryptedCosmosPatchOperations).addAll(patchOperations); + return patchItemInternalHelper(itemId, partitionKey, encryptedCosmosPatchOperations, finalRequestOptions,itemType, false); + }); + } + + @SuppressWarnings("unchecked") // Casting cosmosItemResponse to CosmosItemResponse from CosmosItemResponse + private Mono> patchItemInternalHelper(String itemId, + PartitionKey partitionKey, + CosmosPatchOperations encryptedCosmosPatchOperations, + CosmosPatchItemRequestOptions requestOptions, + Class itemType, + boolean isRetry) { + + setRequestHeaders(requestOptions); + return this.container.patchItem(itemId, partitionKey, encryptedCosmosPatchOperations, requestOptions, itemType).publishOn(encryptionScheduler). + flatMap(cosmosItemResponse -> setByteArrayContent((CosmosItemResponse) cosmosItemResponse, + this.encryptionProcessor.decrypt(this.cosmosItemResponseBuilderAccessor.getByteArrayContent((CosmosItemResponse) cosmosItemResponse))) + .map(bytes -> this.responseFactory.createItemResponse((CosmosItemResponse) cosmosItemResponse, + itemType))).onErrorResume(exception -> { + if (!isRetry && exception instanceof CosmosException) { + final CosmosException cosmosException = (CosmosException) exception; + if (isIncorrectContainerRid(cosmosException)) { + this.encryptionProcessor.getIsEncryptionSettingsInitDone().set(false); + return this.encryptionProcessor.initializeEncryptionSettingsAsync(true).then + (Mono.defer(() -> patchItemInternalHelper(itemId, partitionKey, encryptedCosmosPatchOperations, requestOptions, itemType, true))); + } + } + return Mono.error(exception); + }); + } + /** * Get the CosmosEncryptionAsyncClient * diff --git a/sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/implementation/EncryptionProcessor.java b/sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/implementation/EncryptionProcessor.java index d23c04fac7fe6..728d326cad3a9 100644 --- a/sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/implementation/EncryptionProcessor.java +++ b/sdk/cosmos/azure-cosmos-encryption/src/main/java/com/azure/cosmos/encryption/implementation/EncryptionProcessor.java @@ -242,11 +242,51 @@ public Mono encrypt(byte[] payload) { return encrypt(itemJObj); } - public Mono encrypt(ObjectNode itemJObj) { + public Mono encrypt(JsonNode itemJObj) { return encryptObjectNode(itemJObj).map(encryptedObjectNode -> EncryptionUtils.serializeJsonToByteArray(EncryptionUtils.getSimpleObjectMapper(), encryptedObjectNode)); } - public Mono encryptObjectNode(ObjectNode itemJObj) { + public Mono encryptPatchNode(JsonNode itemObj, String patchPropertyPath) { + assert (itemObj != null); + return initEncryptionSettingsIfNotInitializedAsync().then(Mono.defer(() -> { + for (ClientEncryptionIncludedPath includedPath : this.clientEncryptionPolicy.getIncludedPaths()) { + if (StringUtils.isEmpty(includedPath.getPath()) || includedPath.getPath().charAt(0) != '/' || includedPath.getPath().lastIndexOf('/') != 0) { + return Mono.error(new IllegalArgumentException("Invalid encryption path: " + includedPath.getPath())); + } + } + + for (ClientEncryptionIncludedPath includedPath : this.clientEncryptionPolicy.getIncludedPaths()) { + String propertyName = includedPath.getPath().substring(1); + if (patchPropertyPath.substring(1).equals(propertyName)) { + if (itemObj.isValueNode()) { + return this.encryptionSettings.getEncryptionSettingForPropertyAsync(propertyName, + this).flatMap(settings -> { + try { + return Mono.just(EncryptionUtils.getSimpleObjectMapper().readTree(EncryptionUtils.getSimpleObjectMapper() + .writeValueAsString(encryptAndSerializeValue(settings, + null, itemObj, propertyName)))); + } catch (MicrosoftDataEncryptionException | JsonProcessingException ex) { + return Mono.error(ex); + } + }); + } else { + return this.encryptionSettings.getEncryptionSettingForPropertyAsync(propertyName, + this).flatMap(settings -> { + try { + return Mono.just(encryptAndSerializePatchProperty(settings, + itemObj, propertyName)); + } catch (MicrosoftDataEncryptionException | JsonProcessingException ex) { + return Mono.error(ex); + } + }); + } + } + } + return Mono.empty(); + })); + } + + public Mono encryptObjectNode(JsonNode itemJObj) { assert (itemJObj != null); return initEncryptionSettingsIfNotInitializedAsync().then(Mono.defer(() -> { for (ClientEncryptionIncludedPath includedPath : this.clientEncryptionPolicy.getIncludedPaths()) { @@ -265,7 +305,7 @@ public Mono encryptObjectNode(ObjectNode itemJObj) { this).flatMap(settings -> { try { encryptAndSerializeProperty(settings, itemJObj, propertyValueHolder, propertyName); - } catch (MicrosoftDataEncryptionException ex) { + } catch (MicrosoftDataEncryptionException | JsonProcessingException ex) { return Mono.error(ex); } return Mono.empty(); @@ -278,8 +318,73 @@ public Mono encryptObjectNode(ObjectNode itemJObj) { })); } - public void encryptAndSerializeProperty(EncryptionSettings encryptionSettings, ObjectNode objectNode, - JsonNode propertyValueHolder, String propertyName) throws MicrosoftDataEncryptionException { + @SuppressWarnings("unchecked") + public JsonNode encryptAndSerializePatchProperty(EncryptionSettings encryptionSettings, + JsonNode propertyValueHolder, String propertyName) throws MicrosoftDataEncryptionException, JsonProcessingException { + if (propertyValueHolder.isObject()) { + for (Iterator> it = propertyValueHolder.fields(); it.hasNext(); ) { + Map.Entry child = it.next(); + if (child.getValue().isObject() || child.getValue().isArray()) { + JsonNode encryptedValue = encryptAndSerializePatchProperty(encryptionSettings, child.getValue(), child.getKey()); + assert propertyValueHolder instanceof ObjectNode; + ((ObjectNode) propertyValueHolder).put(child.getKey(), encryptedValue); + } else if (!child.getValue().isNull()){ + assert propertyValueHolder instanceof ObjectNode; + encryptAndSerializeValue(encryptionSettings, (ObjectNode) propertyValueHolder, child.getValue(), + child.getKey()); + } + } + } + + else if (propertyValueHolder.isArray()) { + assert propertyValueHolder instanceof ArrayNode; + ArrayNode arrayNode = (ArrayNode) propertyValueHolder; + if (arrayNode.elements().next().isObject() || arrayNode.elements().next().isArray()) { + List encryptedArray = new ArrayList<>(); + for (Iterator arrayIterator = arrayNode.elements(); arrayIterator.hasNext(); ) { + JsonNode nodeInArray = arrayIterator.next(); + if (nodeInArray.isArray()) { + encryptedArray.add(encryptAndSerializePatchProperty(encryptionSettings, nodeInArray, propertyName)); + } else { + for (Iterator> it = nodeInArray.fields(); it.hasNext(); ) { + Map.Entry child = it.next(); + if (child.getValue().isObject() || child.getValue().isArray()) { + JsonNode encryptedValue = encryptAndSerializePatchProperty(encryptionSettings, + child.getValue(), child.getKey()); + ((ObjectNode) nodeInArray).put(child.getKey(), encryptedValue); + + } else if (!child.getValue().isNull()) { + encryptAndSerializeValue(encryptionSettings, (ObjectNode) nodeInArray, child.getValue(), + child.getKey()); + } + } + encryptedArray.add(nodeInArray); + } + } + arrayNode.removeAll(); + for (JsonNode encryptedValue : encryptedArray) { + arrayNode.add(encryptedValue); + } + } else { + List encryptedArray = new ArrayList<>(); + for (Iterator it = arrayNode.elements(); it.hasNext(); ) { + encryptedArray.add(encryptAndSerializeValue(encryptionSettings, null, it.next(), + StringUtils.EMPTY)); + } + arrayNode.removeAll(); + for (byte[] encryptedValue : encryptedArray) { + arrayNode.add(encryptedValue); + } + } + return arrayNode; + } else { + encryptAndSerializeValue(encryptionSettings, null, propertyValueHolder, propertyName); + } + return propertyValueHolder; + } + + public void encryptAndSerializeProperty(EncryptionSettings encryptionSettings, JsonNode objectNode, + JsonNode propertyValueHolder, String propertyName) throws MicrosoftDataEncryptionException, JsonProcessingException { if (propertyValueHolder.isObject()) { for (Iterator> it = propertyValueHolder.fields(); it.hasNext(); ) { @@ -325,7 +430,7 @@ public void encryptAndSerializeProperty(EncryptionSettings encryptionSettings, O } } } else { - encryptAndSerializeValue(encryptionSettings, objectNode, propertyValueHolder, propertyName); + encryptAndSerializeValue(encryptionSettings, (ObjectNode) objectNode, propertyValueHolder, propertyName); } } @@ -458,7 +563,6 @@ public JsonNode decryptAndSerializeValue(EncryptionSettings encryptionSettings, JsonNode propertyValueHolder, String propertyName) throws MicrosoftDataEncryptionException, IOException { byte[] cipherText; byte[] cipherTextWithTypeMarker; - cipherTextWithTypeMarker = propertyValueHolder.binaryValue(); cipherText = new byte[cipherTextWithTypeMarker.length - 1]; System.arraycopy(cipherTextWithTypeMarker, 1, cipherText, 0, diff --git a/sdk/cosmos/azure-cosmos-encryption/src/test/java/com/azure/cosmos/encryption/EncryptionAsyncApiCrudTest.java b/sdk/cosmos/azure-cosmos-encryption/src/test/java/com/azure/cosmos/encryption/EncryptionAsyncApiCrudTest.java index 42e4a735d030e..d0c7304b22e07 100644 --- a/sdk/cosmos/azure-cosmos-encryption/src/test/java/com/azure/cosmos/encryption/EncryptionAsyncApiCrudTest.java +++ b/sdk/cosmos/azure-cosmos-encryption/src/test/java/com/azure/cosmos/encryption/EncryptionAsyncApiCrudTest.java @@ -23,10 +23,9 @@ import com.azure.cosmos.models.PartitionKey; import com.azure.cosmos.models.SqlParameter; import com.azure.cosmos.models.SqlQuerySpec; +import com.azure.cosmos.models.CosmosPatchOperations; +import com.azure.cosmos.models.CosmosPatchItemRequestOptions; import com.azure.cosmos.util.CosmosPagedFlux; -import com.azure.cosmos.util.CosmosPagedIterable; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.microsoft.data.encryption.cryptography.EncryptionKeyStoreProvider; import io.netty.handler.codec.http.HttpResponseStatus; import org.testng.annotations.AfterClass; @@ -34,6 +33,7 @@ import org.testng.annotations.Factory; import org.testng.annotations.Ignore; import org.testng.annotations.Test; +import reactor.core.publisher.Mono; import java.time.Instant; import java.util.*; @@ -501,6 +501,39 @@ public void crudQueryStaleCache() { assertThat(batchResponse.getResults().size()).isEqualTo(2); validateResponse(createPojo, batchResponse.getResults().get(0).getItem(EncryptionPojo.class)); validateResponse(createPojo, batchResponse.getResults().get(1).getItem(EncryptionPojo.class)); + + //Deleting and creating container + encryptionAsyncContainerOriginal.getCosmosAsyncContainer().delete().block(); + createEncryptionContainer(cosmosEncryptionAsyncDatabase, clientEncryptionPolicy, containerId); + + itemId= UUID.randomUUID().toString(); + createPojo = getItem(itemId); + CosmosItemResponse itemResponse = encryptionAsyncContainerOriginal.createItem(createPojo, + new PartitionKey(createPojo.getMypk()), new CosmosItemRequestOptions()).block(); + + int originalSensitiveInt = createPojo.getSensitiveInt(); + int newSensitiveInt = originalSensitiveInt + 1; + + CosmosPatchOperations cosmosPatchOperations = CosmosPatchOperations.create(); + cosmosPatchOperations.add("/sensitiveString", "patched"); + cosmosPatchOperations.remove("/sensitiveDouble"); + cosmosPatchOperations.replace("/sensitiveInt", newSensitiveInt); + + CosmosItemResponse patchResponse = encryptionAsyncContainerOriginal.patchItem( + createPojo.getId(), + new PartitionKey(createPojo.getMypk()), + cosmosPatchOperations, + new CosmosPatchItemRequestOptions(), + EncryptionPojo.class).block(); + + CosmosItemResponse readResponse = encryptionAsyncContainerOriginal.readItem( + createPojo.getId(), + new PartitionKey(createPojo.getMypk()), + new CosmosPatchItemRequestOptions(), + EncryptionPojo.class).block(); + + validateResponse(patchResponse.getItem(), readResponse.getItem()); + } finally { try { //deleting the database created for this test @@ -526,6 +559,56 @@ public void invalidDataEncryptionKeyAlgorithm() { } } + @Test(groups = {"encryption"}, timeOut = TIMEOUT) + public void patchItem() { + String itemId = UUID.randomUUID().toString(); + EncryptionPojo createPojo = getItem(itemId); + CosmosItemResponse itemResponse = cosmosEncryptionAsyncContainer.createItem(createPojo, + new PartitionKey(createPojo.getMypk()), new CosmosItemRequestOptions()).block(); + + int originalSensitiveInt = createPojo.getSensitiveInt(); + int newSensitiveInt = originalSensitiveInt + 1; + + String itemIdToReplace = UUID.randomUUID().toString(); + EncryptionPojo nestedEncryptionPojoToReplace = getItem(itemIdToReplace); + nestedEncryptionPojoToReplace.setSensitiveString("testing"); + + CosmosPatchOperations cosmosPatchOperations = CosmosPatchOperations.create(); + cosmosPatchOperations.add("/sensitiveString", "patched"); + cosmosPatchOperations.remove("/sensitiveDouble"); + cosmosPatchOperations.replace("/sensitiveInt", newSensitiveInt); + cosmosPatchOperations.replace("/sensitiveNestedPojo", nestedEncryptionPojoToReplace); + cosmosPatchOperations.set("/sensitiveBoolean", false); + + CosmosPatchItemRequestOptions options = new CosmosPatchItemRequestOptions(); + CosmosItemResponse response = this.cosmosEncryptionAsyncContainer.patchItem( + createPojo.getId(), + new PartitionKey(createPojo.getMypk()), + cosmosPatchOperations, + options, + EncryptionPojo.class).block(); + + assertThat(response.getStatusCode()).isEqualTo(HttpResponseStatus.OK.code()); + + EncryptionPojo patchedItem = response.getItem(); + assertThat(patchedItem).isNotNull(); + + assertThat(patchedItem.getSensitiveString()).isEqualTo("patched"); + assertThat(patchedItem.getSensitiveDouble()).isNull(); + assertThat(patchedItem.getSensitiveNestedPojo()).isNotNull(); + assertThat(patchedItem.getSensitiveInt()).isEqualTo(newSensitiveInt); + assertThat(patchedItem.isSensitiveBoolean()).isEqualTo(false); + + response = this.cosmosEncryptionAsyncContainer.readItem( + createPojo.getId(), + new PartitionKey(createPojo.getMypk()), + options, + EncryptionPojo.class).block(); + + assertThat(response.getStatusCode()).isEqualTo(HttpResponseStatus.OK.code()); + validateResponse(patchedItem, response.getItem()); + } + @Test(groups = {"encryption"}, timeOut = TIMEOUT) public void batchExecution() { String itemId= UUID.randomUUID().toString(); diff --git a/sdk/cosmos/azure-cosmos-encryption/src/test/java/com/azure/cosmos/encryption/EncryptionPojo.java b/sdk/cosmos/azure-cosmos-encryption/src/test/java/com/azure/cosmos/encryption/EncryptionPojo.java index 235075d30c1a9..5db2bb8e5db4e 100644 --- a/sdk/cosmos/azure-cosmos-encryption/src/test/java/com/azure/cosmos/encryption/EncryptionPojo.java +++ b/sdk/cosmos/azure-cosmos-encryption/src/test/java/com/azure/cosmos/encryption/EncryptionPojo.java @@ -13,7 +13,7 @@ public class EncryptionPojo { private int sensitiveInt; private float sensitiveFloat; private long sensitiveLong; - private double sensitiveDouble; + private Double sensitiveDouble; private boolean sensitiveBoolean; private EncryptionPojo sensitiveNestedPojo; private int[] sensitiveIntArray; @@ -78,11 +78,11 @@ public void setSensitiveLong(long sensitiveLong) { this.sensitiveLong = sensitiveLong; } - public double getSensitiveDouble() { + public Double getSensitiveDouble() { return sensitiveDouble; } - public void setSensitiveDouble(double sensitiveDouble) { + public void setSensitiveDouble(Double sensitiveDouble) { this.sensitiveDouble = sensitiveDouble; } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/patch/PatchOperation.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/patch/PatchOperation.java index b2a68605d78f7..872d51b3fdc89 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/patch/PatchOperation.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/patch/PatchOperation.java @@ -16,7 +16,7 @@ public abstract class PatchOperation { this.operationType = operationType; } - PatchOperationType getOperationType() { + public PatchOperationType getOperationType() { return operationType; } } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/patch/PatchOperationCore.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/patch/PatchOperationCore.java index a03b189019655..99daf2748cce0 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/patch/PatchOperationCore.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/patch/PatchOperationCore.java @@ -30,11 +30,11 @@ public PatchOperationCore(PatchOperationType operationType, String path, T value this.resource = value; } - String getPath() { + public String getPath() { return path; } - T getResource() { + public T getResource() { return resource; } }