com.azure.cosmos.encryption.CosmosEncryptionAsyncContainer Maven / Gradle / Ivy
Show all versions of azure-cosmos-encryption Show documentation
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.cosmos.encryption;
import com.azure.cosmos.CosmosAsyncContainer;
import com.azure.cosmos.CosmosBridgeInternal;
import com.azure.cosmos.CosmosException;
import com.azure.cosmos.CosmosItemSerializer;
import com.azure.cosmos.encryption.implementation.Constants;
import com.azure.cosmos.encryption.implementation.CosmosEncryptionQueryTransformer;
import com.azure.cosmos.encryption.implementation.CosmosResponseFactory;
import com.azure.cosmos.encryption.implementation.EncryptionImplementationBridgeHelpers;
import com.azure.cosmos.encryption.implementation.EncryptionProcessor;
import com.azure.cosmos.encryption.implementation.EncryptionSettings;
import com.azure.cosmos.encryption.implementation.EncryptionUtils;
import com.azure.cosmos.encryption.implementation.mdesrc.cryptography.MicrosoftDataEncryptionException;
import com.azure.cosmos.encryption.models.SqlQuerySpecWithEncryption;
import com.azure.cosmos.implementation.CosmosBulkExecutionOptionsImpl;
import com.azure.cosmos.implementation.CosmosPagedFluxOptions;
import com.azure.cosmos.implementation.HttpConstants;
import com.azure.cosmos.implementation.ImplementationBridgeHelpers;
import com.azure.cosmos.implementation.JsonSerializable;
import com.azure.cosmos.implementation.ObjectNodeMap;
import com.azure.cosmos.implementation.Strings;
import com.azure.cosmos.implementation.apachecommons.lang.tuple.Pair;
import com.azure.cosmos.implementation.batch.ItemBatchOperation;
import com.azure.cosmos.implementation.batch.ItemBulkOperation;
import com.azure.cosmos.implementation.guava25.base.Preconditions;
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.implementation.query.Transformer;
import com.azure.cosmos.models.CosmosBatch;
import com.azure.cosmos.models.CosmosBatchOperationResult;
import com.azure.cosmos.models.CosmosBatchRequestOptions;
import com.azure.cosmos.models.CosmosBatchResponse;
import com.azure.cosmos.models.CosmosBulkExecutionOptions;
import com.azure.cosmos.models.CosmosBulkItemResponse;
import com.azure.cosmos.models.CosmosBulkOperationResponse;
import com.azure.cosmos.models.CosmosChangeFeedRequestOptions;
import com.azure.cosmos.models.CosmosItemOperation;
import com.azure.cosmos.models.CosmosItemRequestOptions;
import com.azure.cosmos.models.CosmosItemResponse;
import com.azure.cosmos.models.CosmosPatchItemRequestOptions;
import com.azure.cosmos.models.CosmosPatchOperations;
import com.azure.cosmos.models.CosmosQueryRequestOptions;
import com.azure.cosmos.models.FeedResponse;
import com.azure.cosmos.models.ModelBridgeInternal;
import com.azure.cosmos.models.PartitionKey;
import com.azure.cosmos.models.PartitionKeyBuilder;
import com.azure.cosmos.models.SqlParameter;
import com.azure.cosmos.models.SqlQuerySpec;
import com.azure.cosmos.util.CosmosPagedFlux;
import com.azure.cosmos.util.UtilBridgeInternal;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import static com.azure.cosmos.implementation.Utils.getEffectiveCosmosChangeFeedRequestOptions;
import static com.azure.cosmos.implementation.guava25.base.Preconditions.checkNotNull;
/**
* CosmosAsyncContainer with encryption capabilities.
*/
public final class CosmosEncryptionAsyncContainer {
private final Scheduler encryptionScheduler;
private final CosmosResponseFactory responseFactory = new CosmosResponseFactory();
private final CosmosAsyncContainer container;
private final EncryptionProcessor encryptionProcessor;
private final CosmosEncryptionAsyncClient cosmosEncryptionAsyncClient;
private final static ImplementationBridgeHelpers.CosmosItemSerializerHelper.CosmosItemSerializerAccessor itemSerializerAccessor =
ImplementationBridgeHelpers.CosmosItemSerializerHelper.getCosmosItemSerializerAccessor();
private final static ImplementationBridgeHelpers.CosmosItemResponseHelper.CosmosItemResponseBuilderAccessor cosmosItemResponseBuilderAccessor = ImplementationBridgeHelpers.CosmosItemResponseHelper.getCosmosItemResponseBuilderAccessor();
private final static ImplementationBridgeHelpers.CosmosItemRequestOptionsHelper.CosmosItemRequestOptionsAccessor cosmosItemRequestOptionsAccessor = ImplementationBridgeHelpers.CosmosItemRequestOptionsHelper.getCosmosItemRequestOptionsAccessor();
private final static ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.CosmosQueryRequestOptionsAccessor cosmosQueryRequestOptionsAccessor = ImplementationBridgeHelpers.CosmosQueryRequestOptionsHelper.getCosmosQueryRequestOptionsAccessor();
private final static ImplementationBridgeHelpers.CosmosChangeFeedRequestOptionsHelper.CosmosChangeFeedRequestOptionsAccessor cosmosChangeFeedRequestOptionsAccessor = ImplementationBridgeHelpers.CosmosChangeFeedRequestOptionsHelper.getCosmosChangeFeedRequestOptionsAccessor();
private final static ImplementationBridgeHelpers.CosmosAsyncContainerHelper.CosmosAsyncContainerAccessor cosmosAsyncContainerAccessor = ImplementationBridgeHelpers.CosmosAsyncContainerHelper.getCosmosAsyncContainerAccessor();
private final static ImplementationBridgeHelpers.CosmosBatchHelper.CosmosBatchAccessor cosmosBatchAccessor = ImplementationBridgeHelpers.CosmosBatchHelper.getCosmosBatchAccessor();
private final static ImplementationBridgeHelpers.CosmosBatchResponseHelper.CosmosBatchResponseAccessor cosmosBatchResponseAccessor = ImplementationBridgeHelpers.CosmosBatchResponseHelper.getCosmosBatchResponseAccessor();
private final static ImplementationBridgeHelpers.CosmosBatchOperationResultHelper.CosmosBatchOperationResultAccessor cosmosBatchOperationResultAccessor = ImplementationBridgeHelpers.CosmosBatchOperationResultHelper.getCosmosBatchOperationResultAccessor();
private final static ImplementationBridgeHelpers.CosmosBatchRequestOptionsHelper.CosmosBatchRequestOptionsAccessor cosmosBatchRequestOptionsAccessor = ImplementationBridgeHelpers.CosmosBatchRequestOptionsHelper.getCosmosBatchRequestOptionsAccessor();
private final static ImplementationBridgeHelpers.CosmosPatchOperationsHelper.CosmosPatchOperationsAccessor cosmosPatchOperationsAccessor = ImplementationBridgeHelpers.CosmosPatchOperationsHelper.getCosmosPatchOperationsAccessor();
private final static ImplementationBridgeHelpers.CosmosBulkExecutionOptionsHelper.CosmosBulkExecutionOptionsAccessor cosmosBulkExecutionOptionsAccessor = ImplementationBridgeHelpers.CosmosBulkExecutionOptionsHelper.getCosmosBulkExecutionOptionsAccessor();
private final static ImplementationBridgeHelpers.CosmosBulkItemResponseHelper.CosmosBulkItemResponseAccessor cosmosBulkItemResponseAccessor = ImplementationBridgeHelpers.CosmosBulkItemResponseHelper.getCosmosBulkItemResponseAccessor();
private final static EncryptionImplementationBridgeHelpers.SqlQuerySpecWithEncryptionHelper.SqlQuerySpecWithEncryptionAccessor specWithEncryptionAccessor = EncryptionImplementationBridgeHelpers.SqlQuerySpecWithEncryptionHelper.getSqlQuerySpecWithEncryptionAccessor();
CosmosEncryptionAsyncContainer(CosmosAsyncContainer container,
CosmosEncryptionAsyncClient cosmosEncryptionAsyncClient) {
this.container = container;
this.cosmosEncryptionAsyncClient = cosmosEncryptionAsyncClient;
this.encryptionProcessor = new EncryptionProcessor(this.container, cosmosEncryptionAsyncClient);
this.encryptionScheduler = Schedulers.parallel();
}
EncryptionProcessor getEncryptionProcessor() {
return this.encryptionProcessor;
}
/**
* Creates an item.
*
* After subscription the operation will be performed. The {@link Mono} upon
* successful completion will contain a single resource response with the
* created Cosmos item. In case of failure the {@link Mono} will error.
*
* @param the type parameter.
* @param item the Cosmos item represented as a POJO or Cosmos item object.
* @return an {@link Mono} containing the single resource response with the
* created Cosmos item or an error.
*/
@SuppressWarnings("unchecked")
public Mono> createItem(T item) {
return createItem(item, new CosmosItemRequestOptions());
}
/**
* Creates a Cosmos item.
*
* @param the type parameter.
* @param item the item.
* @param requestOptions the item request options.
* @return an {@link Mono} containing the single resource response with the created Cosmos item or an error.
*/
@SuppressWarnings("unchecked")
public Mono> createItem(T item,
CosmosItemRequestOptions requestOptions) {
Preconditions.checkNotNull(item, "item");
if (requestOptions == null) {
requestOptions = new CosmosItemRequestOptions();
}
byte[] streamPayload = cosmosSerializerToStream(item, getEffectiveItemSerializer(requestOptions));
return createItemHelper(streamPayload, requestOptions,(Class) item.getClass(), false );
}
/**
* Creates an item.
*
* After subscription the operation will be performed. The {@link Mono} upon
* successful completion will contain a single resource response with the
* created Cosmos item. In case of failure the {@link Mono} will error.
*
* @param the type parameter.
* @param item the Cosmos item represented as a POJO or Cosmos item object.
* @param partitionKey the partition key.
* @param requestOptions the request options.
* @return an {@link Mono} containing the single resource response with the created Cosmos item or an error.
*/
@SuppressWarnings("unchecked")
public Mono> createItem(T item,
PartitionKey partitionKey,
CosmosItemRequestOptions requestOptions) {
Preconditions.checkNotNull(item, "item");
if (requestOptions == null) {
requestOptions = new CosmosItemRequestOptions();
}
Preconditions.checkArgument(partitionKey != null, "partitionKey cannot be null for operations using "
+ "EncryptionContainer.");
byte[] streamPayload = cosmosSerializerToStream(item, getEffectiveItemSerializer(requestOptions));
return createItemHelper(streamPayload, partitionKey, requestOptions, (Class) item.getClass(), false);
}
/**
* Deletes an item.
*
* After subscription the operation will be performed.
* The {@link Mono} upon successful completion will contain a single Cosmos item response for the deleted item.
*
* @param itemId the item id.
* @param partitionKey the partition key.
* @return an {@link Mono} containing the Cosmos item resource response.
*/
public Mono> deleteItem(String itemId, PartitionKey partitionKey) {
return deleteItem(itemId, partitionKey, new CosmosItemRequestOptions());
}
/**
* Deletes the item.
*
* After subscription the operation will be performed.
* The {@link Mono} upon successful completion will contain a single Cosmos item response for the deleted item.
*
* @param itemId id of the item.
* @param partitionKey partitionKey of the item.
* @param requestOptions the request options.
* @return an {@link Mono} containing the Cosmos item resource response.
*/
public Mono> deleteItem(String itemId,
PartitionKey partitionKey,
CosmosItemRequestOptions requestOptions) {
return deleteItemInternal(itemId, partitionKey, requestOptions);
}
private Mono> deleteItemInternal(String itemId, PartitionKey partitionKey, CosmosItemRequestOptions requestOptions) {
this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync();
return Mono.just(this.encryptionProcessor.getEncryptionSettings())
.flatMap(settings -> {
try {
return Mono.zip(
checkAndGetEncryptedId(itemId, settings),
checkAndGetEncryptedPartitionKey(partitionKey, settings)
).flatMap(encryptedIdPartitionTuple -> container.deleteItem(encryptedIdPartitionTuple.getT1(), encryptedIdPartitionTuple.getT2(), requestOptions));
} catch (Exception ex) {
return Mono.error(ex);
}
});
}
private Mono checkAndGetEncryptedId(String itemId, EncryptionSettings encryptionSettings)
{
if (this.encryptionProcessor.getClientEncryptionPolicy().getIncludedPaths().stream().
anyMatch(includedPath -> includedPath.getPath().substring(1).equals(Constants.PROPERTY_NAME_ID))) {
return this.getEncryptedItem(encryptionSettings, Constants.PROPERTY_NAME_ID, itemId);
}
return Mono.just(itemId);
}
private Mono checkAndGetEncryptedPartitionKey(PartitionKey partitionKey, EncryptionSettings encryptionSettings) {
if (encryptionSettings.getPartitionKeyPaths().isEmpty()) {
return Mono.just(partitionKey);
}
JsonNode partitionKeyNode;
try {
partitionKeyNode = EncryptionUtils.getSimpleObjectMapper().readTree(partitionKey.toString());
} catch (JsonProcessingException ex) {
return Mono.error(ex);
}
if (partitionKeyNode.isArray() && partitionKeyNode.size() > 1) {
ArrayNode arrayNode = (ArrayNode) partitionKeyNode;
return Mono.just(new PartitionKeyBuilder())
.flatMap(partitionKeyBuilder -> Flux.fromIterable(encryptionSettings.getPartitionKeyPaths())
.flatMap(path -> {
// case: partition key path is /a/b/c and the client encryption policy has /a in path.
// hence encrypt the partition key value with using its top level path /a since
// /c would have been encrypted in the document using /a's policy.
String partitionKeyPath = path.split("/")[1];
String childPartitionKey = arrayNode.elements().next().textValue();
if (this.encryptionProcessor.getClientEncryptionPolicy().getIncludedPaths().stream().
anyMatch(includedPath -> includedPath.getPath().substring(1).equals(partitionKeyPath))) {
partitionKeyBuilder.add(childPartitionKey);
return Mono.empty();
}
return getEncryptedItem(encryptionSettings, partitionKeyPath, childPartitionKey);
})
.collectList()
.flatMapMany(Flux::fromIterable)
.doOnNext(partitionKeyBuilder::add)
.then(Mono.just(partitionKeyBuilder.build())));
} else {
return Mono.just(encryptionSettings.getPartitionKeyPaths().get(0))
.flatMap(path -> {
String partitionKeyPath = path.split("/")[1];
if (this.encryptionProcessor.getClientEncryptionPolicy().getIncludedPaths().stream().
noneMatch(includedPath -> includedPath.getPath().substring(1).equals(partitionKeyPath))) {
return Mono.just(partitionKeyNode.elements().next().textValue());
}
return getEncryptedItem(encryptionSettings, partitionKeyPath, partitionKeyNode.elements().next().textValue());
})
.flatMap(encryptedPartitionKey -> Mono.just(new PartitionKey(encryptedPartitionKey)));
}
}
private Mono getEncryptedItem(EncryptionSettings encryptionSettings, String propertyName, String propertyValue) {
return encryptionSettings
.getEncryptionSettingForPropertyAsync(propertyName, this.encryptionProcessor)
.flatMap(settings -> {
try {
return Mono.just(
this.encryptionProcessor.encryptAndSerializeValue(settings, propertyValue, propertyName));
} catch (MicrosoftDataEncryptionException ex) {
return Mono.error(ex);
}
});
}
/**
* Deletes the item.
*
* After subscription the operation will be performed.
* The {@link Mono} upon successful completion will contain a single Cosmos item response for the deleted item.
*
* @param the type parameter.
* @param item item to be deleted.
* @param requestOptions the request options.
* @return an {@link Mono} containing the Cosmos item resource response.
*/
public Mono> deleteItem(T item, CosmosItemRequestOptions requestOptions) {
return container.deleteItem(item, requestOptions);
}
/**
* Deletes all items in the Container with the specified partitionKey value.
* Starts an asynchronous Cosmos DB background operation which deletes all items in the Container with the specified value.
* The asynchronous Cosmos DB background operation runs using a percentage of user RUs.
* After subscription the operation will be performed.
* The {@link Mono} upon successful completion will contain a single Cosmos item response for all the deleted items.
*
* @param partitionKey partitionKey of the item.
* @param requestOptions the request options.
* @return an {@link Mono} containing the Cosmos item resource response.
*/
// TODO Make this api public once it is GA in cosmos core library
Mono> deleteAllItemsByPartitionKey(PartitionKey partitionKey, CosmosItemRequestOptions requestOptions) {
final CosmosItemRequestOptions options = Optional.ofNullable(requestOptions)
.orElse(new CosmosItemRequestOptions());
return deleteAllItemsByPartitionKeyInternal(partitionKey, options);
}
private Mono> deleteAllItemsByPartitionKeyInternal(PartitionKey partitionKey, CosmosItemRequestOptions requestOptions) {
return this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap(encryptedSettings -> checkAndGetEncryptedPartitionKey(partitionKey, encryptedSettings))
.flatMap(encryptedPartitionKey -> container.deleteAllItemsByPartitionKey(encryptedPartitionKey, requestOptions));
}
/**
* Upserts an item.
*
* After subscription the operation will be performed. The {@link Mono} upon
* successful completion will contain a single resource response with the
* upserted item. In case of failure the {@link Mono} will error.
*
* @param the type parameter.
* @param item the item represented as a POJO or Item object to upsert.
* @return an {@link Mono} containing the single resource response with the upserted item or an error.
*/
@SuppressWarnings("unchecked")
public Mono> upsertItem(T item) {
return upsertItem(item, new CosmosItemRequestOptions());
}
/**
* Upserts an item.
*
* After subscription the operation will be performed. The {@link Mono} upon
* successful completion will contain a single resource response with the
* upserted item. In case of failure the {@link Mono} will error.
*
* @param the type parameter.
* @param item the item represented as a POJO or Item object to upsert.
* @param requestOptions the request options.
* @return an {@link Mono} containing the single resource response with the upserted item or an error.
*/
@SuppressWarnings("unchecked")
public Mono> upsertItem(T item, CosmosItemRequestOptions requestOptions) {
Preconditions.checkNotNull(item, "item");
if (requestOptions == null) {
requestOptions = new CosmosItemRequestOptions();
}
byte[] streamPayload = cosmosSerializerToStream(item, getEffectiveItemSerializer(requestOptions));
return upsertItemHelper(streamPayload, requestOptions, (Class) item.getClass(), false);
}
/**
* Upserts an item.
*
* After subscription the operation will be performed. The {@link Mono} upon
* successful completion will contain a single resource response with the
* upserted item. In case of failure the {@link Mono} will error.
*
* @param the type parameter.
* @param item the item represented as a POJO or Item object to upsert.
* @param partitionKey the partition key.
* @param requestOptions the request options.
* @return an {@link Mono} containing the single resource response with the upserted item or an error.
*/
@SuppressWarnings("unchecked")
public Mono> upsertItem(T item,
PartitionKey partitionKey,
CosmosItemRequestOptions requestOptions) {
Preconditions.checkNotNull(item, "item");
if (requestOptions == null) {
requestOptions = new CosmosItemRequestOptions();
}
Preconditions.checkArgument(partitionKey != null, "partitionKey cannot be null for operations using "
+ "EncryptionContainer.");
byte[] streamPayload = cosmosSerializerToStream(item, getEffectiveItemSerializer(requestOptions));
return upsertItemHelper(streamPayload, partitionKey, requestOptions, (Class) item.getClass(), false);
}
/**
* Replaces an item with the passed in item and encrypts the requested fields.
*
* After subscription the operation will be performed.
* The {@link Mono} upon successful completion will contain a single Cosmos item response with the replaced item.
*
* @param the type parameter.
* @param item the item to replace (containing the item id).
* @param itemId the item id.
* @param partitionKey the partition key.
* @return an {@link Mono} containing the Cosmos item resource response with the replaced item or an error.
*/
public Mono> replaceItem(T item, String itemId, PartitionKey partitionKey) {
return replaceItem(item, itemId, partitionKey, new CosmosItemRequestOptions());
}
/**
* Replaces an item with the passed in item and encrypts the requested fields.
*
* After subscription the operation will be performed.
* The {@link Mono} upon successful completion will contain a single Cosmos item response with the replaced item.
*
* @param the type parameter.
* @param item the item to replace (containing the item id).
* @param itemId the item id.
* @param partitionKey the partition key.
* @param requestOptions the request comosItemRequestOptions.
* @return an {@link Mono} containing the Cosmos item resource response with the replaced item or an error.
*/
@SuppressWarnings("unchecked")
public Mono> replaceItem(T item,
String itemId,
PartitionKey partitionKey,
CosmosItemRequestOptions requestOptions) {
Preconditions.checkNotNull(item, "item");
if (requestOptions == null) {
requestOptions = new CosmosItemRequestOptions();
}
Preconditions.checkArgument(partitionKey != null, "partitionKey cannot be null for operations using "
+ "EncryptionContainer.");
byte[] streamPayload = cosmosSerializerToStream(item, getEffectiveItemSerializer(requestOptions));
return replaceItemHelper(streamPayload, itemId, partitionKey, requestOptions, (Class) item.getClass(), false);
}
/**
* Reads an item.
*
* After subscription the operation will be performed.
* The {@link Mono} upon successful completion will contain an item response with the read item.
*
* @param the type parameter.
* @param id the item id.
* @param partitionKey the partition key.
* @param classType the item type.
* @return an {@link Mono} containing the Cosmos item response with the read item or an error.
*/
public Mono> readItem(String id, PartitionKey partitionKey, Class classType) {
return readItem(id, partitionKey, ModelBridgeInternal.createCosmosItemRequestOptions(partitionKey), classType);
}
/**
* Reads an item using a configured {@link CosmosItemRequestOptions}.
*
* After subscription the operation will be performed.
* The {@link Mono} upon successful completion will contain a Cosmos item response with the read item.
*
* @param the type parameter.
* @param id the item id.
* @param partitionKey the partition key.
* @param requestOptions the request {@link CosmosItemRequestOptions}.
* @param classType the item type.
* @return an {@link Mono} containing the Cosmos item response with the read item or an error.
*/
public Mono> readItem(String id,
PartitionKey partitionKey,
CosmosItemRequestOptions requestOptions,
Class classType) {
final CosmosItemRequestOptions options = Optional.ofNullable(requestOptions)
.orElse(new CosmosItemRequestOptions());
Mono> responseMessageMono = this.readItemHelper(id, partitionKey, options, false);
CosmosItemSerializer effectiveItemSerializer =
cosmosEncryptionAsyncClient.getEffectiveItemSerializer(options.getCustomItemSerializer());
return responseMessageMono.publishOn(encryptionScheduler).flatMap(cosmosItemResponse -> setByteArrayContent(cosmosItemResponse,
this.encryptionProcessor.decrypt(
cosmosItemResponseBuilderAccessor.getByteArrayContent(cosmosItemResponse),
CosmosItemSerializer.DEFAULT_SERIALIZER))
.map(bytes -> this.responseFactory.createItemResponse(cosmosItemResponse, classType, effectiveItemSerializer)));
}
/**
* Query for items in the current container.
*
* After subscription the operation will be performed. The {@link CosmosPagedFlux} will
* contain one or several feed response of the obtained items. In case of
* failure the {@link CosmosPagedFlux} will error.
*
* @param the type parameter.
* @param query the query.
* @param classType the class type.
* @return a {@link CosmosPagedFlux} containing one or several feed response pages of the obtained items or an
* error.
*/
public CosmosPagedFlux queryItems(String query, Class classType) {
return this.queryItems(new SqlQuerySpec(query), classType);
}
/**
* Query for items in the current container using a string.
*
* After subscription the operation will be performed. The {@link CosmosPagedFlux} will
* contain one or several feed response of the obtained items. In case of
* failure the {@link CosmosPagedFlux} will error.
*
* @param the type parameter.
* @param query the query.
* @param requestOptions the query request options.
* @param classType the class type.
* @return a {@link CosmosPagedFlux} containing one or several feed response pages of the obtained items or an
* error.
*/
public CosmosPagedFlux queryItems(String query, CosmosQueryRequestOptions requestOptions,
Class classType) {
if (requestOptions == null) {
requestOptions = new CosmosQueryRequestOptions();
}
return this.queryItems(new SqlQuerySpec(query), requestOptions, classType);
}
/**
* Query for items in the current container using a {@link SqlQuerySpec}.
*
* After subscription the operation will be performed. The {@link CosmosPagedFlux} will
* contain one or several feed response of the obtained items. In case of
* failure the {@link CosmosPagedFlux} will error.
*
* @param the type parameter.
* @param querySpec the SQL query specification.
* @param classType the class type.
* @return a {@link CosmosPagedFlux} containing one or several feed response pages of the obtained items or an
* error.
*/
public CosmosPagedFlux queryItems(SqlQuerySpec querySpec, Class classType) {
return queryItemsHelper(querySpec, new CosmosQueryRequestOptions(), classType, false);
}
/**
* Query for items in the current container using a {@link SqlQuerySpec} and {@link CosmosQueryRequestOptions}.
*
* After subscription the operation will be performed. The {@link Flux} will
* contain one or several feed response of the obtained items. In case of
* failure the {@link CosmosPagedFlux} will error.
*
* @param the type parameter.
* @param query the SQL query specification.
* @param requestOptions the query request options.
* @param classType the class type.
* @return a {@link CosmosPagedFlux} containing one or several feed response pages of the obtained items or an
* error.
*/
public CosmosPagedFlux queryItems(SqlQuerySpec query, CosmosQueryRequestOptions requestOptions,
Class classType) {
if (requestOptions == null) {
requestOptions = new CosmosQueryRequestOptions();
}
return queryItemsHelper(query, requestOptions, classType,false);
}
/**
* Query for items in the current container using a {@link SqlQuerySpecWithEncryption}.
*
* After subscription the operation will be performed. The {@link CosmosPagedFlux} will contain one or several feed
* response of the obtained items. In case of failure the {@link CosmosPagedFlux} will error.
*
* @param the type parameter.
* @param sqlQuerySpecWithEncryption the sqlQuerySpecWithEncryption.
* @param options the query request options.
* @param classType the class type.
* @return a {@link CosmosPagedFlux} containing one or several feed response pages of the obtained items or an
* error.
*/
public CosmosPagedFlux queryItemsOnEncryptedProperties(SqlQuerySpecWithEncryption sqlQuerySpecWithEncryption,
CosmosQueryRequestOptions options,
Class classType) {
if (options == null) {
options = new CosmosQueryRequestOptions();
}
if (specWithEncryptionAccessor.getEncryptionParamMap(sqlQuerySpecWithEncryption).size() > 0) {
List> encryptionSqlParameterMonoList = new ArrayList<>();
for (Map.Entry entry :
specWithEncryptionAccessor.getEncryptionParamMap(sqlQuerySpecWithEncryption).entrySet()) {
encryptionSqlParameterMonoList.add(specWithEncryptionAccessor.addEncryptionParameterAsync(sqlQuerySpecWithEncryption, entry.getKey(), entry.getValue(), this));
}
Mono> listMono = Flux.mergeSequential(encryptionSqlParameterMonoList).collectList();
Mono sqlQuerySpecMono =
listMono.flatMap(ignoreVoids -> Mono.just(specWithEncryptionAccessor.getSqlQuerySpec(sqlQuerySpecWithEncryption)));
return queryItemsHelperWithMonoSqlQuerySpec(sqlQuerySpecMono, sqlQuerySpecWithEncryption, options, classType, false);
} else {
return queryItemsHelper(specWithEncryptionAccessor.getSqlQuerySpec(sqlQuerySpecWithEncryption),
options, classType, false);
}
}
/**
* Query for items in the change feed of the current container using the {@link CosmosChangeFeedRequestOptions}.
*
* After subscription the operation will be performed. The {@link Flux} will
* contain one or several feed response of the obtained items. In case of
* failure the {@link CosmosPagedFlux} will error.
*
* @param the type parameter.
* @param options the change feed request options.
* @param classType the class type.
* @return a {@link CosmosPagedFlux} containing one or several feed response pages of the obtained
* items or an error.
*/
// TODO Make this api public once it is GA in cosmos core library
CosmosPagedFlux queryChangeFeed(CosmosChangeFeedRequestOptions options, Class classType) {
checkNotNull(options, "Argument 'options' must not be null.");
checkNotNull(classType, "Argument 'classType' must not be null.");
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");
final CosmosPatchItemRequestOptions patchOptions = Optional.ofNullable(options)
.orElse(new CosmosPatchItemRequestOptions());
return patchItemHelper(itemId, partitionKey, cosmosPatchOperations, patchOptions, itemType);
}
private Mono> patchItemHelper(String itemId,
PartitionKey partitionKey,
CosmosPatchOperations cosmosPatchOperations,
CosmosPatchItemRequestOptions options,
Class itemType) {
this.setRequestHeaders(options);
List> monoList = new ArrayList<>();
List operations = cosmosPatchOperationsAccessor.getPatchOperations(cosmosPatchOperations);
List operationsSnapshot;
synchronized (operations) {
operationsSnapshot = new ArrayList<>(operations);
}
for (PatchOperation patchOperation : operationsSnapshot) {
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 -> {
List snapshot =
cosmosPatchOperationsAccessor.getPatchOperations(encryptedCosmosPatchOperations);
synchronized(snapshot) {
snapshot.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);
CosmosItemSerializer effectiveItemSerializer =
cosmosEncryptionAsyncClient.getEffectiveItemSerializer(requestOptions.getCustomItemSerializer());
CosmosPatchItemRequestOptions requestOptionsWithDefaultSerializer = requestOptions != null
? (CosmosPatchItemRequestOptions)cosmosItemRequestOptionsAccessor
.clonePatchItemRequestOptions(requestOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER)
: new CosmosPatchItemRequestOptions();
return this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap(encryptionSettings -> Mono.zip(
checkAndGetEncryptedId(itemId, encryptionSettings),
checkAndGetEncryptedPartitionKey(partitionKey, encryptionSettings))
.flatMap(encryptedIdPartitionKeyTuple ->
this.container.patchItem(encryptedIdPartitionKeyTuple.getT1(), encryptedIdPartitionKeyTuple.getT2(), encryptedCosmosPatchOperations, requestOptionsWithDefaultSerializer, itemType).publishOn(encryptionScheduler).
flatMap(cosmosItemResponse -> setByteArrayContent((CosmosItemResponse) cosmosItemResponse,
this.encryptionProcessor.decrypt(
cosmosItemResponseBuilderAccessor.getByteArrayContent((CosmosItemResponse) cosmosItemResponse),
CosmosItemSerializer.DEFAULT_SERIALIZER))
.map(bytes -> this.responseFactory.createItemResponse((CosmosItemResponse) cosmosItemResponse,
itemType, effectiveItemSerializer))).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
*
* @return encrypted cosmosAsyncClient
*/
CosmosEncryptionAsyncClient getCosmosEncryptionAsyncClient() {
return cosmosEncryptionAsyncClient;
}
/**
* Gets the CosmosAsyncContainer
*
* @return cosmos container
*/
public CosmosAsyncContainer getCosmosAsyncContainer() {
return container;
}
byte[] cosmosSerializerToStream(T item, CosmosItemSerializer effectiveSerializer) {
return EncryptionUtils.serializeJsonToByteArray(effectiveSerializer, item);
}
CosmosItemSerializer getEffectiveItemSerializer(CosmosItemRequestOptions requestOptions) {
return getEffectiveItemSerializer(
requestOptions != null ? requestOptions.getCustomItemSerializer() : null);
}
CosmosItemSerializer getEffectiveItemSerializer(CosmosItemSerializer requestLevelItemSerializer) {
return CosmosBridgeInternal
.getAsyncDocumentClient(container.getDatabase())
.getEffectiveItemSerializer(requestLevelItemSerializer);
}
Mono decryptResponseNode(
JsonNode jsonNode) {
if (jsonNode == null) {
return Mono.empty();
}
return this.encryptionProcessor.decryptJsonNode(
jsonNode);
}
private Mono> setByteArrayContent(CosmosItemResponse rsp,
Mono> bytesMono) {
return bytesMono.flatMap(
bytes -> {
cosmosItemResponseBuilderAccessor.setByteArrayContent(rsp, bytes);
return Mono.just(rsp);
}
).defaultIfEmpty(rsp);
}
private Mono> readItemHelper(String id,
PartitionKey partitionKey,
CosmosItemRequestOptions requestOptions,
boolean isRetry) {
this.setRequestHeaders(requestOptions);
Mono> responseMessageMono = this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap(encryptionSettings -> Mono.zip(
checkAndGetEncryptedId(id, encryptionSettings),
checkAndGetEncryptedPartitionKey(partitionKey, encryptionSettings))
.flatMap(encryptedIdPartitionKeyTuple ->
this.container.readItem(
encryptedIdPartitionKeyTuple.getT1(),
encryptedIdPartitionKeyTuple.getT2(),
requestOptions, byte[].class)));
return responseMessageMono.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(() -> readItemHelper(id, partitionKey, requestOptions, true)
));
}
}
return Mono.error(exception);
});
}
private Mono> createItemHelper(byte[] streamPayload,
CosmosItemRequestOptions requestOptions,
Class itemClass,
boolean isRetry) {
this.setRequestHeaders(requestOptions);
CosmosItemSerializer effectiveItemSerializer =
cosmosEncryptionAsyncClient.getEffectiveItemSerializer(requestOptions.getCustomItemSerializer());
// The actual replace happens on the already encrypted document
// so any custom serialization/deserialization happens here in the encryption wrapper
CosmosItemRequestOptions requestOptionsWithDefaultSerializer = ModelBridgeInternal
.clone(requestOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER);
return this.encryptionProcessor.encrypt(streamPayload)
.flatMap(encryptedPayload -> this.container.createItem(
encryptedPayload,
requestOptionsWithDefaultSerializer)
.publishOn(encryptionScheduler)
.flatMap(cosmosItemResponse -> setByteArrayContent(cosmosItemResponse,
this.encryptionProcessor.decrypt(
cosmosItemResponseBuilderAccessor.getByteArrayContent(cosmosItemResponse),
CosmosItemSerializer.DEFAULT_SERIALIZER))
.map(bytes -> this.responseFactory.createItemResponse(cosmosItemResponse,
itemClass, effectiveItemSerializer))).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(() -> createItemHelper(streamPayload, requestOptions,
itemClass, true)));
}
}
return Mono.error(exception);
}));
}
private Mono> createItemHelper(byte[] streamPayload,
PartitionKey partitionKey,
CosmosItemRequestOptions requestOptions,
Class itemClass,
boolean isRetry) {
this.setRequestHeaders(requestOptions);
CosmosItemSerializer effectiveItemSerializer =
cosmosEncryptionAsyncClient.getEffectiveItemSerializer(requestOptions.getCustomItemSerializer());
// The actual replace happens on the already encrypted document
// so any custom serialization/deserialization happens here in the encryption wrapper
CosmosItemRequestOptions requestOptionsWithDefaultSerializer = ModelBridgeInternal
.clone(requestOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER);
AtomicReference encryptedPK = new AtomicReference<>();
Mono encryptedPayloadMono =
this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap(encryptionSettings -> checkAndGetEncryptedPartitionKey(partitionKey, encryptionSettings))
.flatMap(encryptedPartitionKey -> {
encryptedPK.set(encryptedPartitionKey);
return this.encryptionProcessor.encrypt(streamPayload);
});
return encryptedPayloadMono
.flatMap(encryptedPayload -> this.container.createItem(
encryptedPayload,
encryptedPK.get(),
requestOptionsWithDefaultSerializer)
.publishOn(encryptionScheduler)
.flatMap(cosmosItemResponse -> setByteArrayContent(cosmosItemResponse,
this.encryptionProcessor.decrypt(
cosmosItemResponseBuilderAccessor.getByteArrayContent(cosmosItemResponse),
CosmosItemSerializer.DEFAULT_SERIALIZER))
.map(bytes -> this.responseFactory.createItemResponse(cosmosItemResponse,
itemClass, effectiveItemSerializer))).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(() -> createItemHelper(streamPayload, partitionKey, requestOptions,
itemClass, true)));
}
}
return Mono.error(exception);
}));
}
private Mono> upsertItemHelper(byte[] streamPayload,
CosmosItemRequestOptions requestOptions,
Class itemClass,
boolean isRetry) {
this.setRequestHeaders(requestOptions);
CosmosItemSerializer effectiveItemSerializer =
cosmosEncryptionAsyncClient.getEffectiveItemSerializer(requestOptions.getCustomItemSerializer());
// The actual replace happens on the already encrypted document
// so any custom serialization/deserialization happens her in the encryption wrapper
CosmosItemRequestOptions requestOptionsWithDefaultSerializer = ModelBridgeInternal
.clone(requestOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER);
return this.encryptionProcessor.encrypt(streamPayload)
.flatMap(encryptedPayload -> this.container.upsertItem(
encryptedPayload,
requestOptionsWithDefaultSerializer)
.publishOn(encryptionScheduler)
.flatMap(cosmosItemResponse -> setByteArrayContent(cosmosItemResponse,
this.encryptionProcessor.decrypt(
cosmosItemResponseBuilderAccessor.getByteArrayContent(cosmosItemResponse),
CosmosItemSerializer.DEFAULT_SERIALIZER))
.map(bytes -> this.responseFactory.createItemResponse(cosmosItemResponse, itemClass, effectiveItemSerializer)))
.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(() -> upsertItemHelper(streamPayload, requestOptions,
itemClass, true)));
}
}
return Mono.error(exception);
}));
}
private Mono> upsertItemHelper(byte[] streamPayload,
PartitionKey partitionKey,
CosmosItemRequestOptions requestOptions,
Class itemClass,
boolean isRetry) {
this.setRequestHeaders(requestOptions);
CosmosItemSerializer effectiveItemSerializer =
cosmosEncryptionAsyncClient.getEffectiveItemSerializer(requestOptions.getCustomItemSerializer());
// The actual replace happens on the already encrypted document
// so any custom serialization/deserialization happens her in the encryption wrapper
CosmosItemRequestOptions requestOptionsWithDefaultSerializer = ModelBridgeInternal
.clone(requestOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER);
AtomicReference encryptedPK = new AtomicReference<>();
Mono encryptedPayloadMono = this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap(encryptionSettings -> checkAndGetEncryptedPartitionKey(partitionKey, encryptionSettings))
.flatMap(encryptedPartitionKey -> {
encryptedPK.set(encryptedPartitionKey);
return this.encryptionProcessor.encrypt(streamPayload);
});
return encryptedPayloadMono
.flatMap(encryptedPayload -> this.container.upsertItem(
encryptedPayload,
encryptedPK.get(),
requestOptionsWithDefaultSerializer)
.publishOn(encryptionScheduler)
.flatMap(cosmosItemResponse -> setByteArrayContent(cosmosItemResponse,
this.encryptionProcessor.decrypt(
cosmosItemResponseBuilderAccessor.getByteArrayContent(cosmosItemResponse),
CosmosItemSerializer.DEFAULT_SERIALIZER))
.map(bytes -> this.responseFactory.createItemResponse(cosmosItemResponse, itemClass, effectiveItemSerializer)))
.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(() -> upsertItemHelper(streamPayload, partitionKey, requestOptions,
itemClass, true)));
}
}
return Mono.error(exception);
}));
}
private Mono> replaceItemHelper(byte[] streamPayload,
String itemId,
PartitionKey partitionKey,
CosmosItemRequestOptions requestOptions,
Class itemClass,
boolean isRetry) {
this.setRequestHeaders(requestOptions);
CosmosItemSerializer effectiveItemSerializer =
cosmosEncryptionAsyncClient.getEffectiveItemSerializer(requestOptions.getCustomItemSerializer());
AtomicReference encryptedPK = new AtomicReference<>();
AtomicReference encryptedId = new AtomicReference<>();
Mono encryptedPayloadMono = this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap(encryptionSettings -> Mono.zip(
checkAndGetEncryptedId(itemId, encryptionSettings),
checkAndGetEncryptedPartitionKey(partitionKey, encryptionSettings)))
.flatMap(encryptedIdPartitionKeyTuple -> {
encryptedId.set(encryptedIdPartitionKeyTuple.getT1());
encryptedPK.set(encryptedIdPartitionKeyTuple.getT2());
return this.encryptionProcessor.encrypt(streamPayload);
});
// The actual replace happens on the already encrypted document
// so any custom serialization/deserialization happens her in the encryption wrapper
CosmosItemRequestOptions requestOptionsWithDefaultSerializer = ModelBridgeInternal
.clone(requestOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER);
return encryptedPayloadMono
.flatMap(encryptedPayload -> this.container.replaceItem(
encryptedPayload,
encryptedId.get(),
encryptedPK.get(),
requestOptionsWithDefaultSerializer)
.publishOn(encryptionScheduler)
.flatMap(cosmosItemResponse -> setByteArrayContent(cosmosItemResponse,
this.encryptionProcessor.decrypt(
cosmosItemResponseBuilderAccessor.getByteArrayContent(cosmosItemResponse),
CosmosItemSerializer.DEFAULT_SERIALIZER))
.map(bytes -> this.responseFactory.createItemResponse(cosmosItemResponse, itemClass, effectiveItemSerializer)))
.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(() -> replaceItemHelper(streamPayload, itemId, partitionKey, requestOptions,
itemClass, true)));
}
}
return Mono.error(exception);
}));
}
private CosmosPagedFlux queryItemsHelper(SqlQuerySpec sqlQuerySpec,
CosmosQueryRequestOptions options,
Class classType,
boolean isRetry) {
CosmosItemSerializer effectiveSerializer = this
.getCosmosEncryptionAsyncClient()
.getEffectiveItemSerializer(options != null ? options.getCustomItemSerializer(): null);
return UtilBridgeInternal.createCosmosPagedFlux(pagedFluxOptions -> {
AtomicBoolean shouldRetry = new AtomicBoolean(!isRetry);
Transformer transformer = new CosmosEncryptionQueryTransformer(
this.encryptionScheduler,
this.getEncryptionProcessor(),
classType,
false);
Flux> result = this.transformQueryItemsInternal(
transformer,
sqlQuerySpec,
options,
effectiveSerializer
).apply(pagedFluxOptions);
return result
.onErrorResume(exception -> {
if (exception instanceof CosmosException) {
final CosmosException cosmosException = (CosmosException) exception;
if (shouldRetry.get() && isIncorrectContainerRid(cosmosException)) {
// stale cache, refresh caches and then retry
this.encryptionProcessor.getIsEncryptionSettingsInitDone().set(false);
shouldRetry.set(false);
return this.encryptionProcessor
.initializeEncryptionSettingsAsync(true)
.thenMany(
Flux.defer(() -> {
return this.transformQueryItemsInternal(
transformer,
sqlQuerySpec,
options,
effectiveSerializer
).apply(pagedFluxOptions);
})
);
}
}
return Mono.error(exception);
});
});
}
private Function>> transformQueryItemsInternal(
Transformer transformer,
SqlQuerySpec sqlQuerySpec,
CosmosQueryRequestOptions queryRequestOptions,
CosmosItemSerializer effectiveSerializer) {
CosmosQueryRequestOptions finalOptions = setRequestHeaders(cosmosQueryRequestOptionsAccessor
.clone(queryRequestOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER));
return transformer.transform(
cosmosAsyncContainerAccessor.queryItemsInternalFunc(
this.container,
sqlQuerySpec,
finalOptions,
JsonNode.class),
effectiveSerializer
);
}
private Function>> transformQueryChangeFeedInternal(
Transformer transformer,
CosmosChangeFeedRequestOptions changeFeedRequestOptions,
CosmosPagedFluxOptions pagedFluxOptions,
CosmosItemSerializer effectiveSerializer) {
CosmosChangeFeedRequestOptions finalOptions = setRequestHeaders(
cosmosChangeFeedRequestOptionsAccessor.clone(changeFeedRequestOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER));
getEffectiveCosmosChangeFeedRequestOptions(pagedFluxOptions, finalOptions);
return transformer.transform(
cosmosAsyncContainerAccessor
.queryChangeFeedInternalFunc(
this.container,
finalOptions,
JsonNode.class),
effectiveSerializer
);
}
private Function>> transformQueryItemsWithMonoSqlQuerySpec(
Transformer transformer,
Mono sqlQuerySpecMono,
CosmosQueryRequestOptions options,
CosmosItemSerializer effectiveSerializer) {
CosmosQueryRequestOptions finalOptions = setRequestHeaders(cosmosQueryRequestOptionsAccessor
.clone(options)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER));
return transformer.transform(
cosmosAsyncContainerAccessor.queryItemsInternalFuncWithMonoSqlQuerySpec(
this.container,
sqlQuerySpecMono,
finalOptions,
JsonNode.class
),
effectiveSerializer
);
}
private CosmosPagedFlux queryChangeFeedHelper(CosmosChangeFeedRequestOptions options,
Class classType,
boolean isRetry) {
CosmosItemSerializer effectiveSerializer = this
.getCosmosEncryptionAsyncClient()
.getEffectiveItemSerializer(options != null ? options.getCustomItemSerializer() : null);
return UtilBridgeInternal.createCosmosPagedFlux(pagedFluxOptions -> {
AtomicBoolean shouldRetry = new AtomicBoolean(!isRetry);
Transformer transformer = new CosmosEncryptionQueryTransformer(
this.encryptionScheduler,
this.getEncryptionProcessor(),
classType,
true);
Flux> result = this.transformQueryChangeFeedInternal(
transformer,
options,
pagedFluxOptions,
effectiveSerializer
).apply(pagedFluxOptions);
return result
.onErrorResume(exception -> {
if (exception instanceof CosmosException) {
final CosmosException cosmosException = (CosmosException) exception;
if (shouldRetry.get() && isIncorrectContainerRid(cosmosException)) {
// stale cache, refresh caches and then retry
this.encryptionProcessor.getIsEncryptionSettingsInitDone().set(false);
shouldRetry.set(false);
return this.encryptionProcessor
.initializeEncryptionSettingsAsync(true)
.thenMany(
Flux.defer(() -> {
return this.transformQueryChangeFeedInternal(
transformer,
options,
pagedFluxOptions,
effectiveSerializer
).apply(pagedFluxOptions);
})
);
}
}
return Mono.error(exception);
});
});
}
private CosmosPagedFlux queryItemsHelperWithMonoSqlQuerySpec(Mono sqlQuerySpecMono,
SqlQuerySpecWithEncryption sqlQuerySpecWithEncryption,
CosmosQueryRequestOptions options,
Class classType,
boolean isRetry) {
CosmosItemSerializer effectiveSerializer = this
.getCosmosEncryptionAsyncClient()
.getEffectiveItemSerializer(options != null ? options.getCustomItemSerializer(): null);
return UtilBridgeInternal.createCosmosPagedFlux(pagedFluxOptions -> {
AtomicBoolean shouldRetry = new AtomicBoolean(!isRetry);
Transformer transformer = new CosmosEncryptionQueryTransformer(
this.encryptionScheduler,
this.getEncryptionProcessor(),
classType,
false);
Flux> result = this.transformQueryItemsWithMonoSqlQuerySpec(
transformer,
sqlQuerySpecMono,
options,
effectiveSerializer
).apply(pagedFluxOptions);
return result
.onErrorResume(exception -> {
if (exception instanceof CosmosException) {
final CosmosException cosmosException = (CosmosException) exception;
if (shouldRetry.get() && isIncorrectContainerRid(cosmosException)) {
// stale cache, refresh caches and then retry
this.encryptionProcessor.getIsEncryptionSettingsInitDone().set(false);
shouldRetry.set(false);
return this.encryptionProcessor
.initializeEncryptionSettingsAsync(true)
.thenMany(
Flux.defer(() -> this.transformQueryItemsInternal(
transformer,
specWithEncryptionAccessor.getSqlQuerySpec(sqlQuerySpecWithEncryption),
options,
effectiveSerializer
).apply(pagedFluxOptions)
)
);
}
}
return Mono.error(exception);
});
});
}
/**
* Executes the encrypted transactional batch.
*
* @param cosmosBatch Batch having list of operation and partition key which will be executed by this container.
*
* @return A Mono response which contains details of execution of the transactional batch.
*
* If the transactional batch executes successfully, the value returned by {@link
* CosmosBatchResponse#getStatusCode} on the response returned will be set to 200}.
*
* If an operation within the transactional batch fails during execution, no changes from the batch will be
* committed and the status of the failing operation is made available by {@link
* CosmosBatchResponse#getStatusCode} or by the exception. To obtain information about the operations
* that failed in case of some user error like conflict, not found etc, the response can be enumerated.
* This returns {@link CosmosBatchOperationResult} instances corresponding to each operation in the
* transactional batch in the order they were added to the transactional batch.
* For a result corresponding to an operation within the transactional batch, use
* {@link CosmosBatchOperationResult#getStatusCode}
* to access the status of the operation. If the operation was not executed or it was aborted due to the failure of
* another operation within the transactional batch, the value of this field will be 424;
* for the operation that caused the batch to abort, the value of this field
* will indicate the cause of failure.
*
* If there are issues such as request timeouts, Gone, session not available, network failure
* or if the service somehow returns 5xx then the Mono will return error instead of CosmosBatchResponse.
*
* Use {@link CosmosBatchResponse#isSuccessStatusCode} on the response returned to ensure that the
* transactional batch succeeded.
*/
public Mono executeCosmosBatch(CosmosBatch cosmosBatch) {
return this.executeCosmosBatch(cosmosBatch, new CosmosBatchRequestOptions());
}
/**
* Executes the encrypted transactional batch.
*
* @param cosmosBatch Batch having list of operation and partition key which will be executed by this container.
* @param requestOptions Options that apply specifically to batch request.
*
* @return A Mono response which contains details of execution of the transactional batch.
*
* If the transactional batch executes successfully, the value returned by {@link
* CosmosBatchResponse#getStatusCode} on the response returned will be set to 200}.
*
* If an operation within the transactional batch fails during execution, no changes from the batch will be
* committed and the status of the failing operation is made available by {@link
* CosmosBatchResponse#getStatusCode} or by the exception. To obtain information about the operations
* that failed in case of some user error like conflict, not found etc, the response can be enumerated.
* This returns {@link CosmosBatchOperationResult} instances corresponding to each operation in the
* transactional batch in the order they were added to the transactional batch.
* For a result corresponding to an operation within the transactional batch, use
* {@link CosmosBatchOperationResult#getStatusCode}
* to access the status of the operation. If the operation was not executed or it was aborted due to the failure of
* another operation within the transactional batch, the value of this field will be 424;
* for the operation that caused the batch to abort, the value of this field
* will indicate the cause of failure.
*
* If there are issues such as request timeouts, Gone, session not available, network failure
* or if the service somehow returns 5xx then the Mono will return error instead of CosmosBatchResponse.
*
* Use {@link CosmosBatchResponse#isSuccessStatusCode} on the response returned to ensure that the
* transactional batch succeeded.
*/
public Mono executeCosmosBatch(CosmosBatch cosmosBatch, CosmosBatchRequestOptions requestOptions) {
final CosmosBatchRequestOptions cosmosBatchRequestOptions = Optional.ofNullable(requestOptions)
.orElse(new CosmosBatchRequestOptions());
final CosmosItemSerializer effectiveItemSerializer = this
.getEffectiveItemSerializer(cosmosBatchRequestOptions.getCustomItemSerializer());
List>> monoList = new ArrayList<>();
for (ItemBatchOperation> itemBatchOperation : cosmosBatchAccessor.getOperationsInternal(cosmosBatch)) {
Mono> itemBatchOperationMono = null;
if (itemBatchOperation.getItem() != null) {
itemBatchOperationMono = this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap(encryptionSettings -> {
try {
String idValue = getIdValue(itemBatchOperation);
return Mono.zip(
checkAndGetEncryptedId(idValue, encryptionSettings),
checkAndGetEncryptedPartitionKey(itemBatchOperation.getPartitionKeyValue(), encryptionSettings));
} catch (IllegalAccessException | NoSuchFieldException e) {
return Mono.error(e);
}
})
.flatMap(encryptedIdPartitionKeyTuple -> {
Map jsonTree = itemSerializerAccessor.serializeSafe(
effectiveItemSerializer,
itemBatchOperation.getItem());
ObjectNode objectNode = jsonTree instanceof ObjectNodeMap
? ((ObjectNodeMap)jsonTree).getObjectNode().deepCopy()
: EncryptionUtils.getSimpleObjectMapper().valueToTree(jsonTree);
return encryptionProcessor.encryptObjectNode(objectNode).map(encryptedItem -> new ItemBatchOperation<>(
itemBatchOperation.getOperationType(),
encryptedIdPartitionKeyTuple.getT1(),
encryptedIdPartitionKeyTuple.getT2(),
itemBatchOperation.getRequestOptions(),
encryptedItem
));
});
} else {
itemBatchOperationMono = this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap(encryptionSettings -> {
return Mono.zip(
checkAndGetEncryptedId(itemBatchOperation.getId(), encryptionSettings),
checkAndGetEncryptedPartitionKey(itemBatchOperation.getPartitionKeyValue(), encryptionSettings));
})
.flatMap(encryptedIdPartitionKeyTuple -> Mono.just(
new ItemBatchOperation<>(
itemBatchOperation.getOperationType(),
encryptedIdPartitionKeyTuple.getT1(),
encryptedIdPartitionKeyTuple.getT2(),
itemBatchOperation.getRequestOptions(),
null
)));
}
monoList.add(itemBatchOperationMono);
}
Mono>> encryptedOperationListMono =
Flux.mergeSequential(monoList).collectList();
CosmosBatch encryptedCosmosBatch = CosmosBatch.createCosmosBatch(cosmosBatch.getPartitionKeyValue());
CosmosBatchRequestOptions batchRequestOptionsWithDefaultSerializer = cosmosBatchRequestOptionsAccessor
.clone(cosmosBatchRequestOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER);
return encryptedOperationListMono.flatMap(itemBatchOperations -> {
cosmosBatchAccessor.getOperationsInternal(encryptedCosmosBatch).addAll(itemBatchOperations);
return executeCosmosBatchHelper(
encryptedCosmosBatch, batchRequestOptionsWithDefaultSerializer, effectiveItemSerializer, false);
});
}
private Mono executeCosmosBatchHelper(CosmosBatch encryptedCosmosBatch,
CosmosBatchRequestOptions requestOptions,
CosmosItemSerializer effectiveItemSerializer,
boolean isRetry) {
setRequestHeaders(requestOptions);
return this.container.executeCosmosBatch(encryptedCosmosBatch, requestOptions).flatMap(cosmosBatchResponse -> {
// TODO this should check for BadRequest StatusCode too, requires a service fix to return 400 instead of
// -1 which is currently returned inside the body.
// Once fixed from service below if condition can be removed, as this is already covered in onErrorResume.
if (!isRetry && cosmosBatchResponse.getSubStatusCode() == 1024) {
this.encryptionProcessor.getIsEncryptionSettingsInitDone().set(false);
return this.encryptionProcessor.initializeEncryptionSettingsAsync(true).then
(Mono.defer(() -> executeCosmosBatchHelper(
encryptedCosmosBatch, requestOptions, effectiveItemSerializer, true)));
}
List> decryptMonoList = new ArrayList<>();
for (CosmosBatchOperationResult cosmosBatchOperationResult :
cosmosBatchResponseAccessor.getResults(cosmosBatchResponse)) {
ObjectNode objectNode =
cosmosBatchOperationResultAccessor.getResourceObject(cosmosBatchOperationResult);
if (objectNode != null) {
decryptMonoList.add(encryptionProcessor.decryptJsonNode(objectNode).flatMap(jsonNode -> {
cosmosBatchOperationResultAccessor.setResourceObject(cosmosBatchOperationResult, (ObjectNode) jsonNode);
cosmosBatchOperationResultAccessor.setEffectiveItemSerializer(
cosmosBatchOperationResult,
effectiveItemSerializer
);
return Mono.empty();
}));
}
}
Mono> listMono = Flux.mergeSequential(decryptMonoList).collectList();
return listMono.map(aVoid -> cosmosBatchResponse);
}).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(() -> executeCosmosBatchHelper(
encryptedCosmosBatch, requestOptions, effectiveItemSerializer, true)));
}
}
return Mono.error(exception);
});
}
/**
* Executes flux of operations in Bulk.
*
* @param The context for the bulk processing.
* @param operations Flux of operation which will be executed by this container.
*
* @return A Flux of {@link CosmosBulkOperationResponse} which contains operation and it's response or exception.
*
* To create a operation which can be executed here, use {@link com.azure.cosmos.models.CosmosBulkOperations}. For eg.
* for a upsert operation use {@link com.azure.cosmos.models.CosmosBulkOperations#getUpsertItemOperation(Object, PartitionKey)}
*
*
* We can get the corresponding operation using {@link CosmosBulkOperationResponse#getOperation()} and
* it's response using {@link CosmosBulkOperationResponse#getResponse()}. If the operation was executed
* successfully, the value returned by {@link com.azure.cosmos.models.CosmosBulkItemResponse#isSuccessStatusCode()} will be true. To get
* actual status use {@link com.azure.cosmos.models.CosmosBulkItemResponse#getStatusCode()}.
*
* To check if the operation had any exception, use {@link CosmosBulkOperationResponse#getException()} to
* get the exception.
*/
public Flux> executeBulkOperations(
Flux operations) {
return this.executeBulkOperations(operations, new CosmosBulkExecutionOptions());
}
private static String getIdValue(CosmosItemOperation itemOperation)
throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException {
String idSnapshot = itemOperation.getId();
if (!Strings.isNullOrEmpty(idSnapshot)) {
return idSnapshot;
}
Class> itemClass = itemOperation.getItem().getClass();
if (ObjectNode.class.isAssignableFrom(itemClass)) {
return ((ObjectNode)itemOperation.getItem()).get("id").textValue();
}
if (JsonSerializable.class.isAssignableFrom(itemClass)) {
return ((JsonSerializable)itemOperation.getItem()).get(Constants.PROPERTY_NAME_ID).toString();
}
Field id = itemClass.getDeclaredField(Constants.PROPERTY_NAME_ID);
id.setAccessible(true);
return (String) id.get(itemOperation.getItem());
}
/**
* Executes flux of operations in Bulk.
*
* @param The context for the bulk processing.
*
* @param operations Flux of operation which will be executed by this container.
* @param bulkOptions Options that apply for this Bulk request which specifies options regarding execution like
* concurrency, batching size, interval and context.
*
* @return A Flux of {@link CosmosBulkOperationResponse} which contains operation and it's response or exception.
*
* To create a operation which can be executed here, use {@link com.azure.cosmos.models.CosmosBulkOperations}. For eg.
* for a upsert operation use {@link com.azure.cosmos.models.CosmosBulkOperations#getUpsertItemOperation(Object, PartitionKey)}
*
*
* We can get the corresponding operation using {@link CosmosBulkOperationResponse#getOperation()} and
* it's response using {@link CosmosBulkOperationResponse#getResponse()}. If the operation was executed
* successfully, the value returned by {@link com.azure.cosmos.models.CosmosBulkItemResponse#isSuccessStatusCode()} will be true. To get
* actual status use {@link com.azure.cosmos.models.CosmosBulkItemResponse#getStatusCode()}.
*
* To check if the operation had any exception, use {@link CosmosBulkOperationResponse#getException()} to
* get the exception.
*/
@SuppressWarnings("unchecked")
public Flux> executeBulkOperations(
Flux operations,
CosmosBulkExecutionOptions bulkOptions) {
final CosmosBulkExecutionOptions cosmosBulkExecutionOptions = Optional.ofNullable(bulkOptions)
.orElse(new CosmosBulkExecutionOptions());
CosmosItemSerializer effectiveItemSerializer =
this.getEffectiveItemSerializer(cosmosBulkExecutionOptions.getCustomItemSerializer());
Flux operationFlux = operations.flatMap(cosmosItemOperation -> {
Mono cosmosItemOperationMono = null;
if (cosmosItemOperation.getItem() != null) {
cosmosItemOperationMono = this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap( encryptionSettings -> {
try {
String idValue = getIdValue(cosmosItemOperation);
return Mono.zip(
checkAndGetEncryptedId(idValue, encryptionSettings),
checkAndGetEncryptedPartitionKey(cosmosItemOperation.getPartitionKeyValue(), encryptionSettings));
} catch (IllegalAccessException | NoSuchFieldException e) {
return Mono.error(e);
}
})
.flatMap(encryptedIdPartitionKeyTuple -> {
Map jsonTree = itemSerializerAccessor.serializeSafe(
effectiveItemSerializer,
cosmosItemOperation.getItem());
ObjectNode objectNode = jsonTree instanceof ObjectNodeMap
? ((ObjectNodeMap)jsonTree).getObjectNode().deepCopy()
: EncryptionUtils.getSimpleObjectMapper().valueToTree(jsonTree);
assert cosmosItemOperation instanceof ItemBulkOperation;
return this.encryptionProcessor.encryptObjectNode(objectNode).map(encryptedItem -> new ItemBulkOperation<>(
cosmosItemOperation.getOperationType(),
encryptedIdPartitionKeyTuple.getT1(),
encryptedIdPartitionKeyTuple.getT2(),
((ItemBulkOperation) cosmosItemOperation).getRequestOptions(),
encryptedItem,
cosmosItemOperation.getContext()
));
});
} else {
cosmosItemOperationMono = this.encryptionProcessor.initEncryptionSettingsIfNotInitializedAsync()
.thenReturn(this.encryptionProcessor.getEncryptionSettings())
.flatMap( encryptionSettings -> Mono.zip(
checkAndGetEncryptedId(cosmosItemOperation.getId() , encryptionSettings),
checkAndGetEncryptedPartitionKey(cosmosItemOperation.getPartitionKeyValue(), encryptionSettings)))
.flatMap(encryptedIdPartitionKeyTuple -> Mono.just(
new ItemBulkOperation<>(
cosmosItemOperation.getOperationType(),
encryptedIdPartitionKeyTuple.getT1(),
encryptedIdPartitionKeyTuple.getT2(),
((ItemBulkOperation) cosmosItemOperation).getRequestOptions(),
null,
cosmosItemOperation.getContext()
)));
}
return cosmosItemOperationMono;
});
Mono> listMono = operationFlux.collectList();
setRequestHeaders(cosmosBulkExecutionOptions);
operationFlux = listMono.flatMapMany(Flux::fromIterable);
CosmosBulkExecutionOptions executionOptionsWithDefaultSerializer = cosmosBulkExecutionOptionsAccessor
.clone(cosmosBulkExecutionOptions)
.setCustomItemSerializer(CosmosItemSerializer.DEFAULT_SERIALIZER);
return executeBulkOperationsHelper(
operationFlux, executionOptionsWithDefaultSerializer, effectiveItemSerializer);
}
@SuppressWarnings("unchecked")
private Flux> executeBulkOperationsHelper(Flux operations,
CosmosBulkExecutionOptions bulkOptions,
CosmosItemSerializer effectiveItemSerializer) {
return this.container.executeBulkOperations(operations, bulkOptions).flatMap(cosmosBulkOperationResponse -> {
CosmosBulkItemResponse cosmosBulkItemResponse = cosmosBulkOperationResponse.getResponse();
ObjectNode objectNode = cosmosBulkItemResponseAccessor.getResourceObject(cosmosBulkItemResponse);
if(objectNode != null) {
Mono jsonNodeMono = encryptionProcessor.decryptJsonNode(objectNode).flatMap(jsonNode -> {
cosmosBulkItemResponseAccessor.setResourceObject(cosmosBulkItemResponse,
(ObjectNode) jsonNode);
cosmosBulkItemResponseAccessor.setEffectiveItemSerializer(cosmosBulkItemResponse, effectiveItemSerializer);
return Mono.just(jsonNode);
});
return jsonNodeMono.flux().flatMap(jsonNode -> Flux.just((CosmosBulkOperationResponse) cosmosBulkOperationResponse));
}
return Mono.just((CosmosBulkOperationResponse) cosmosBulkOperationResponse);
});
}
private void setRequestHeaders(CosmosItemRequestOptions requestOptions) {
cosmosItemRequestOptionsAccessor.setHeader(requestOptions, Constants.IS_CLIENT_ENCRYPTED_HEADER, "true");
cosmosItemRequestOptionsAccessor.setHeader(requestOptions, Constants.INTENDED_COLLECTION_RID_HEADER, this.encryptionProcessor.getContainerRid());
}
private CosmosQueryRequestOptions setRequestHeaders(CosmosQueryRequestOptions requestOptions) {
cosmosQueryRequestOptionsAccessor
.getImpl(requestOptions)
.setHeader(
Constants.IS_CLIENT_ENCRYPTED_HEADER,
"true");
cosmosQueryRequestOptionsAccessor
.getImpl(requestOptions)
.setHeader(
Constants.INTENDED_COLLECTION_RID_HEADER,
this.encryptionProcessor.getContainerRid());
return requestOptions;
}
private CosmosChangeFeedRequestOptions setRequestHeaders(CosmosChangeFeedRequestOptions requestOptions) {
cosmosChangeFeedRequestOptionsAccessor.setHeader(requestOptions, Constants.IS_CLIENT_ENCRYPTED_HEADER, "true");
cosmosChangeFeedRequestOptionsAccessor.setHeader(requestOptions, Constants.INTENDED_COLLECTION_RID_HEADER, this.encryptionProcessor.getContainerRid());
return requestOptions;
}
private void setRequestHeaders(CosmosBatchRequestOptions requestOptions) {
cosmosBatchRequestOptionsAccessor.setHeader(requestOptions, Constants.IS_CLIENT_ENCRYPTED_HEADER, "true");
cosmosBatchRequestOptionsAccessor.setHeader(requestOptions, Constants.INTENDED_COLLECTION_RID_HEADER, this.encryptionProcessor.getContainerRid());
}
private void setRequestHeaders(CosmosBulkExecutionOptions requestOptions) {
CosmosBulkExecutionOptionsImpl requestOptionsImpl = cosmosBulkExecutionOptionsAccessor.getImpl(requestOptions);
requestOptionsImpl.setHeader(Constants.IS_CLIENT_ENCRYPTED_HEADER, "true");
requestOptionsImpl.setHeader(Constants.INTENDED_COLLECTION_RID_HEADER, this.encryptionProcessor.getContainerRid());
}
boolean isIncorrectContainerRid(CosmosException cosmosException) {
return cosmosException.getStatusCode() == HttpConstants.StatusCodes.BADREQUEST &&
cosmosException.getResponseHeaders().get(HttpConstants.HttpHeaders.SUB_STATUS) != null &&
cosmosException.getResponseHeaders().get(HttpConstants.HttpHeaders.SUB_STATUS)
.equals(Constants.INCORRECT_CONTAINER_RID_SUB_STATUS);
}
static {
EncryptionImplementationBridgeHelpers.CosmosEncryptionAsyncContainerHelper.setCosmosEncryptionAsyncContainerAccessor(new EncryptionImplementationBridgeHelpers.CosmosEncryptionAsyncContainerHelper.CosmosEncryptionAsyncContainerAccessor() {
@Override
public EncryptionProcessor getEncryptionProcessor(CosmosEncryptionAsyncContainer cosmosEncryptionAsyncContainer) {
return cosmosEncryptionAsyncContainer.getEncryptionProcessor();
}
});
}
}