From c27120b1d669f163461c96570759f9edef989757 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Mon, 24 May 2021 17:50:37 -0500 Subject: [PATCH 01/16] Introduced TableTransactionAction and TableTransactionActionType. Created the submitTransaction() and submitTransactionWithResponse() methods in TableAsyncClient and TableClient. --- sdk/tables/azure-data-tables/CHANGELOG.md | 11 + .../azure/data/tables/TableAsyncClient.java | 258 +++++++++++++++--- .../com/azure/data/tables/TableClient.java | 45 +++ .../azure/data/tables/TableClientBuilder.java | 4 +- .../tables/models/TableTransactionAction.java | 66 +++++ .../models/TableTransactionActionType.java | 44 +++ 6 files changed, 392 insertions(+), 36 deletions(-) create mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java create mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java diff --git a/sdk/tables/azure-data-tables/CHANGELOG.md b/sdk/tables/azure-data-tables/CHANGELOG.md index 6a61bb8abf545..f106795e3ef86 100644 --- a/sdk/tables/azure-data-tables/CHANGELOG.md +++ b/sdk/tables/azure-data-tables/CHANGELOG.md @@ -2,6 +2,17 @@ ## 12.0.0-beta.8 (Unreleased) +### New Features + +- Introduced the `TableTransactionAction` class and the `TableTransactionActionType` enum. + +### Breaking Changes + +- Removed the `TableBatch` and `TableAsyncBatch` types, as well as the methods `TableAsyncClient.createBatch()` and `TableClient.createBatch()`. In their place, batch operations can now be submitted via the following methods: + - `TableAsyncClient.submitTransaction(List transactionalBatch)` + - `TableAsyncClient.submitTransactionWithResponse(List transactionalBatch)` + - `TableClient.submitTransaction(List transactionalBatch)` + - `TableClient.submitTransactionWithResponse(List transactionalBatch, Duration timeout, Context context)` ## 12.0.0-beta.7 (2021-05-15) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index 117a9fc36cc3c..d2b24403dab2a 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -16,13 +16,20 @@ import com.azure.core.util.Context; import com.azure.core.util.FluxUtil; import com.azure.core.util.IterableStream; +import com.azure.core.util.ServiceVersion; import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.serializer.SerializerAdapter; import com.azure.data.tables.implementation.AzureTableImpl; import com.azure.data.tables.implementation.AzureTableImplBuilder; +import com.azure.data.tables.implementation.BatchImpl; import com.azure.data.tables.implementation.ModelHelper; import com.azure.data.tables.implementation.TableUtils; import com.azure.data.tables.implementation.models.AccessPolicy; +import com.azure.data.tables.implementation.models.BatchChangeSet; +import com.azure.data.tables.implementation.models.BatchOperation; +import com.azure.data.tables.implementation.models.BatchRequestBody; +import com.azure.data.tables.implementation.models.BatchSubRequest; +import com.azure.data.tables.implementation.models.BatchSubmitBatchResponse; import com.azure.data.tables.implementation.models.OdataMetadataFormat; import com.azure.data.tables.implementation.models.QueryOptions; import com.azure.data.tables.implementation.models.ResponseFormat; @@ -30,6 +37,8 @@ import com.azure.data.tables.implementation.models.TableEntityQueryResponse; import com.azure.data.tables.implementation.models.TableProperties; import com.azure.data.tables.implementation.models.TableResponseProperties; +import com.azure.data.tables.implementation.models.TableServiceError; +import com.azure.data.tables.models.BatchOperationResponse; import com.azure.data.tables.models.ListEntitiesOptions; import com.azure.data.tables.models.TableAccessPolicy; import com.azure.data.tables.models.TableEntity; @@ -37,9 +46,13 @@ import com.azure.data.tables.models.TableItem; import com.azure.data.tables.models.TableServiceException; import com.azure.data.tables.models.TableSignedIdentifier; +import com.azure.data.tables.models.TableTransactionAction; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -51,6 +64,7 @@ import static com.azure.core.util.FluxUtil.pagedFluxError; import static com.azure.core.util.FluxUtil.withContext; import static com.azure.data.tables.implementation.TableUtils.swallowExceptionForStatusCode; +import static com.azure.data.tables.implementation.TableUtils.toTableServiceError; /** * Provides an asynchronous service client for accessing a table in the Azure Tables service. @@ -68,15 +82,15 @@ public final class TableAsyncClient { private static final String DELIMITER_CONTINUATION_TOKEN = ";"; private final ClientLogger logger = new ClientLogger(TableAsyncClient.class); private final String tableName; - private final AzureTableImpl implementation; - private final SerializerAdapter serializerAdapter; + private final AzureTableImpl tablesImplementation; + private final BatchImpl batchImplementation; private final String accountName; private final String tableEndpoint; private final HttpPipeline pipeline; + private final TableAsyncClient batchPrepClient; - private TableAsyncClient(String tableName, AzureTableImpl implementation, SerializerAdapter serializerAdapter) { - this.serializerAdapter = serializerAdapter; - + TableAsyncClient(String tableName, HttpPipeline pipeline, String serviceUrl, TableServiceVersion serviceVersion, + SerializerAdapter tablesSerializer, SerializerAdapter batchSerializer) { try { if (tableName == null) { throw new NullPointerException("'tableName' must not be null to create a TableClient."); @@ -86,7 +100,7 @@ private TableAsyncClient(String tableName, AzureTableImpl implementation, Serial throw new IllegalArgumentException("'tableName' must not be empty to create a TableClient."); } - final URI uri = URI.create(implementation.getUrl()); + final URI uri = URI.create(serviceUrl); this.accountName = uri.getHost().split("\\.", 2)[0]; this.tableEndpoint = uri.resolve("/" + tableName).toString(); @@ -95,21 +109,33 @@ private TableAsyncClient(String tableName, AzureTableImpl implementation, Serial throw logger.logExceptionAsError(ex); } - this.implementation = implementation; + this.tablesImplementation = new AzureTableImplBuilder() + .url(serviceUrl) + .serializerAdapter(tablesSerializer) + .pipeline(pipeline) + .version(serviceVersion.getVersion()) + .buildClient(); + this.batchImplementation = new BatchImpl(tablesImplementation, batchSerializer); this.tableName = tableName; - this.pipeline = implementation.getHttpPipeline(); + this.pipeline = tablesImplementation.getHttpPipeline(); + this.batchPrepClient = new TableAsyncClient(this, serviceVersion, tablesSerializer); } - TableAsyncClient(String tableName, HttpPipeline pipeline, String serviceUrl, TableServiceVersion serviceVersion, - SerializerAdapter serializerAdapter) { - this(tableName, new AzureTableImplBuilder() - .url(serviceUrl) - .serializerAdapter(serializerAdapter) - .pipeline(pipeline) - .version(serviceVersion.getVersion()) - .buildClient(), - serializerAdapter - ); + // Create a hollow client to be used for obtaining the body of a batch operation to submit. + TableAsyncClient(TableAsyncClient client, ServiceVersion serviceVersion, SerializerAdapter tablesSerializer) { + this.accountName = client.getAccountName(); + this.tableEndpoint = client.getTableEndpoint(); + this.pipeline = BuilderHelper.buildNullClientPipeline(); + this.tablesImplementation = new AzureTableImplBuilder() + .url(client.getTablesImplementation().getUrl()) + .serializerAdapter(tablesSerializer) + .pipeline(this.pipeline) + .version(serviceVersion.getVersion()) + .buildClient(); + this.tableName = client.getTableName(); + // A batch prep client does not need its own batch prep client nor batch implementation. + this.batchImplementation = null; + this.batchPrepClient = null; } /** @@ -153,8 +179,8 @@ HttpPipeline getHttpPipeline() { * * @return This client's {@link AzureTableImpl}. */ - AzureTableImpl getImplementation() { - return implementation; + AzureTableImpl getTablesImplementation() { + return tablesImplementation; } /** @@ -163,7 +189,7 @@ AzureTableImpl getImplementation() { * @return The REST API version used by this client. */ public TableServiceVersion getServiceVersion() { - return TableServiceVersion.fromString(implementation.getVersion()); + return TableServiceVersion.fromString(tablesImplementation.getVersion()); } /** @@ -218,7 +244,7 @@ Mono> createTableWithResponse(Context context) { final TableProperties properties = new TableProperties().setTableName(tableName); try { - return implementation.getTables().createWithResponseAsync(properties, null, + return tablesImplementation.getTables().createWithResponseAsync(properties, null, ResponseFormat.RETURN_NO_CONTENT, null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> @@ -271,7 +297,7 @@ Mono> createEntityWithResponse(TableEntity entity, Context contex EntityHelper.setPropertiesFromGetters(entity, logger); try { - return implementation.getTables().insertEntityWithResponseAsync(tableName, null, null, + return tablesImplementation.getTables().insertEntityWithResponseAsync(tableName, null, null, ResponseFormat.RETURN_NO_CONTENT, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> @@ -359,14 +385,14 @@ Mono> upsertEntityWithResponse(TableEntity entity, TableEntityUpd try { if (updateMode == TableEntityUpdateMode.REPLACE) { - return implementation.getTables().updateEntityWithResponseAsync(tableName, entity.getPartitionKey(), + return tablesImplementation.getTables().updateEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null, null, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), null)); } else { - return implementation.getTables().mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), + return tablesImplementation.getTables().mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null, null, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> @@ -463,8 +489,8 @@ public Mono> updateEntityWithResponse(TableEntity entity, TableEn return withContext(context -> updateEntityWithResponse(entity, updateMode, ifUnchanged, context)); } - Mono> updateEntityWithResponse(TableEntity entity, TableEntityUpdateMode updateMode, boolean ifUnchanged, - Context context) { + Mono> updateEntityWithResponse(TableEntity entity, TableEntityUpdateMode updateMode, + boolean ifUnchanged, Context context) { context = context == null ? Context.NONE : context; if (entity == null) { @@ -476,14 +502,14 @@ Mono> updateEntityWithResponse(TableEntity entity, TableEntityUpd try { if (updateMode == TableEntityUpdateMode.REPLACE) { - return implementation.getTables().updateEntityWithResponseAsync(tableName, entity.getPartitionKey(), + return tablesImplementation.getTables().updateEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null, eTag, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), null)); } else { - return implementation.getTables().mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), + return tablesImplementation.getTables().mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null, eTag, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> @@ -523,7 +549,7 @@ Mono> deleteTableWithResponse(Context context) { context = context == null ? Context.NONE : context; try { - return implementation.getTables().deleteWithResponseAsync(tableName, null, context) + return tablesImplementation.getTables().deleteWithResponseAsync(tableName, null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> (Response) new SimpleResponse(response, null)) .onErrorResume(TableServiceException.class, e -> swallowExceptionForStatusCode(404, e, logger)); @@ -592,7 +618,7 @@ Mono> deleteEntityWithResponse(String partitionKey, String rowKey } try { - return implementation.getTables().deleteEntityWithResponseAsync(tableName, partitionKey, rowKey, matchParam, + return tablesImplementation.getTables().deleteEntityWithResponseAsync(tableName, partitionKey, rowKey, matchParam, null, null, null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> (Response) new SimpleResponse(response, null)) @@ -739,7 +765,7 @@ private Mono> listEntities(String nextP .setFormat(OdataMetadataFormat.APPLICATION_JSON_ODATA_FULLMETADATA); try { - return implementation.getTables().queryEntitiesWithResponseAsync(tableName, null, null, + return tablesImplementation.getTables().queryEntitiesWithResponseAsync(tableName, null, null, nextPartitionKey, nextRowKey, queryOptions, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .flatMap(response -> { @@ -952,7 +978,7 @@ Mono> getEntityWithResponse(String partition } try { - return implementation.getTables().queryEntityWithPartitionAndRowKeyWithResponseAsync(tableName, + return tablesImplementation.getTables().queryEntityWithPartitionAndRowKeyWithResponseAsync(tableName, partitionKey, rowKey, null, null, queryOptions, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .handle((response, sink) -> { @@ -996,7 +1022,7 @@ PagedFlux getAccessPolicy(Context context) { Context finalContext = context; Function>> retriever = marker -> - implementation.getTables().getAccessPolicyWithResponseAsync(tableName, null, null, finalContext) + tablesImplementation.getTables().getAccessPolicyWithResponseAsync(tableName, null, null, finalContext) .map(response -> new PagedResponseBase<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), @@ -1055,7 +1081,7 @@ Mono> setAccessPolicyWithResponse(List tab context = context == null ? Context.NONE : context; try { - return implementation.getTables() + return tablesImplementation.getTables() .setAccessPolicyWithResponseAsync(tableName, null, null, tableSignedIdentifiers.stream().map(this::toSignedIdentifier).collect(Collectors.toList()), context) .map(response -> new SimpleResponse<>(response, response.getValue())); @@ -1076,4 +1102,166 @@ private AccessPolicy toAccessPolicy(TableAccessPolicy tableAccessPolicy) { .setStart(tableAccessPolicy.getStartsOn()) .setPermission(tableAccessPolicy.getPermissions()); } + + /** + * Executes all operations within the batch inside a transaction. When the call completes, either all operations in + * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation + * in a batch must operate on a distinct row key. Attempting to add multiple operations to a batch that share the + * same row key will cause an error. + * + * @param transactionalBatch A list of {@link TableTransactionAction transaction actions} to perform on entities + * in a table. + * + * @return A reactive result containing a list of {@link BatchOperationResponse sub-responses} for each operation + * in the batch. + * + * @throws IllegalStateException If no operations have been added to the batch. + * @throws TableServiceException If any operation within the batch fails. See the documentation for other client + * methods in {@link TableAsyncClient} to understand the conditions that may cause a given operation to fail. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public synchronized Mono> submitTransaction(List transactionalBatch) { + return submitTransactionWithResponse(transactionalBatch) + .flatMap(response -> Mono.justOrEmpty(response.getValue())); + } + + /** + * Executes all operations within the batch inside a transaction. When the call completes, either all operations in + * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation + * in a batch must operate on a distinct row key. Attempting to add multiple operations to a batch that share the + * same row key will cause an error. + * + * @param transactionalBatch A list of {@link TableTransactionAction transaction actions} to perform on entities + * in a table. + * + * @return A reactive result containing the HTTP response produced for the batch itself. The response's value will + * contain a list of {@link BatchOperationResponse sub-responses} for each operation in the batch. + * + * @throws IllegalStateException If no operations have been added to the batch. + * @throws TableServiceException if any operation within the batch fails. See the documentation for the client + * methods in {@link TableAsyncClient} to understand the conditions that may cause a given operation to fail. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public synchronized Mono>> submitTransactionWithResponse(List transactionalBatch) { + return withContext(context -> submitTransactionWithResponse(transactionalBatch, context)); + } + + Mono>> submitTransactionWithResponse(List transactionalBatch, Context context) { + Context finalContext = context == null ? Context.NONE : context; + + if (transactionalBatch.size() == 0) { + throw logger.logExceptionAsError(new IllegalStateException("A batch must contain at least one operation.")); + } + + final List operations = new ArrayList<>(); + + for (TableTransactionAction transactionAction : transactionalBatch) { + switch (transactionAction.getActionType()) { + case CREATE: + operations.add(new BatchOperation.CreateEntity(transactionAction.getEntity())); + + break; + case UPSERT_MERGE: + operations.add(new BatchOperation.UpsertEntity(transactionAction.getEntity(), + TableEntityUpdateMode.MERGE)); + + break; + case UPSERT_REPLACE: + operations.add(new BatchOperation.UpsertEntity(transactionAction.getEntity(), + TableEntityUpdateMode.REPLACE)); + + break; + case UPDATE_MERGE: + operations.add(new BatchOperation.UpdateEntity(transactionAction.getEntity(), + TableEntityUpdateMode.MERGE, transactionAction.getIfUnchanged())); + + break; + case UPDATE_REPLACE: + operations.add(new BatchOperation.UpdateEntity(transactionAction.getEntity(), + TableEntityUpdateMode.REPLACE, transactionAction.getIfUnchanged())); + + break; + case DELETE: + operations.add(new BatchOperation.DeleteEntity(transactionAction.getEntity().getPartitionKey(), + transactionAction.getEntity().getRowKey(), transactionAction.getEntity().getETag())); + + break; + } + } + + return Flux.fromIterable(operations) + .flatMapSequential(op -> op.prepareRequest(batchPrepClient).zipWith(Mono.just(op))) + .collect(BatchRequestBody::new, (body, pair) -> + body.addChangeOperation(new BatchSubRequest(pair.getT2(), pair.getT1()))) + .flatMap(body -> + batchImplementation.submitBatchWithRestResponseAsync(body, null, finalContext).zipWith(Mono.just(body))) + .flatMap(pair -> parseResponse(pair.getT2(), pair.getT1())); + } + + private Mono>> parseResponse(BatchRequestBody requestBody, + BatchSubmitBatchResponse response) { + TableServiceError error = null; + String errorMessage = null; + BatchChangeSet changes = null; + BatchOperation failedOperation = null; + + if (requestBody.getContents().get(0) instanceof BatchChangeSet) { + changes = (BatchChangeSet) requestBody.getContents().get(0); + } + + for (int i = 0; i < response.getValue().length; i++) { + BatchOperationResponse subResponse = response.getValue()[i]; + + // Attempt to attach a sub-request to each batch sub-response + if (changes != null && changes.getContents().get(i) != null) { + ModelHelper.updateBatchOperationResponse(subResponse, + changes.getContents().get(i).getHttpRequest()); + } + + // If one sub-response was an error, we need to throw even though the service responded with 202 + if (subResponse.getStatusCode() >= 400 && error == null && errorMessage == null) { + if (subResponse.getValue() instanceof TableServiceError) { + error = (TableServiceError) subResponse.getValue(); + + // Make a best effort to locate the failed operation and include it in the message + if (changes != null && error.getOdataError() != null + && error.getOdataError().getMessage() != null + && error.getOdataError().getMessage().getValue() != null) { + + String message = error.getOdataError().getMessage().getValue(); + + try { + int failedIndex = Integer.parseInt(message.substring(0, message.indexOf(":"))); + failedOperation = changes.getContents().get(failedIndex).getOperation(); + } catch (NumberFormatException e) { + // Unable to parse failed operation from batch error message - this just means + // the service did not indicate which request was the one that failed. Since + // this is optional, just swallow the exception. + } + } + } else if (subResponse.getValue() instanceof String) { + errorMessage = "The service returned the following data for the failed operation: " + + subResponse.getValue(); + } else { + errorMessage = + "The service returned the following status code for the failed operation: " + + subResponse.getStatusCode(); + } + } + } + + if (error != null || errorMessage != null) { + String message = "An operation within the batch failed, the transaction has been rolled back."; + + if (failedOperation != null) { + message += " The failed operation was: " + failedOperation; + } else if (errorMessage != null) { + message += " " + errorMessage; + } + + return monoError(logger, new TableServiceException(message, null, toTableServiceError(error))); + } else { + return Mono.just(new SimpleResponse<>(response, Arrays.asList(response.getValue()))); + } + } } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java index a601e6f1dc107..911ea2b5e398c 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java @@ -8,12 +8,14 @@ import com.azure.core.http.rest.PagedIterable; import com.azure.core.http.rest.Response; import com.azure.core.util.Context; +import com.azure.data.tables.models.BatchOperationResponse; import com.azure.data.tables.models.ListEntitiesOptions; import com.azure.data.tables.models.TableEntity; import com.azure.data.tables.models.TableEntityUpdateMode; import com.azure.data.tables.models.TableItem; import com.azure.data.tables.models.TableServiceException; import com.azure.data.tables.models.TableSignedIdentifier; +import com.azure.data.tables.models.TableTransactionAction; import java.time.Duration; import java.util.List; @@ -520,4 +522,47 @@ public Response setAccessPolicyWithResponse(List ta Duration timeout, Context context) { return blockWithOptionalTimeout(client.setAccessPolicyWithResponse(tableSignedIdentifiers, context), timeout); } + + /** + * Executes all operations within the batch inside a transaction. When the call completes, either all operations in + * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation + * in a batch must operate on a distinct row key. Attempting to add multiple operations to a batch that share the + * same row key will cause an error. + * + * @param transactionalBatch A list of {@link TableTransactionAction transaction actions} to perform on entities + * in a table. + * + * @return A list of {@link BatchOperationResponse sub-responses} for each operation in the batch. + * + * @throws IllegalStateException If no operations have been added to the batch. + * @throws TableServiceException if any operation within the batch fails. See the documentation for the client + * methods in {@link TableClient} to understand the conditions that may cause a given operation to fail. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public List submitTransaction(List transactionalBatch) { + return client.submitTransaction(transactionalBatch).block(); + } + + /** + * Executes all operations within the batch inside a transaction. When the call completes, either all operations in + * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation + * in a batch must operate on a distinct row key. Attempting to add multiple operations to a batch that share the + * same row key will cause an error. + * + * @param transactionalBatch A list of {@link TableTransactionAction transaction actions} to perform on entities + * in a table. + * @param timeout Duration to wait for the operation to complete. + * @param context Additional context that is passed through the HTTP pipeline during the service call. + * + * @return An HTTP response produced for the batch itself. The response's value will contain a list of + * {@link BatchOperationResponse sub-responses} for each operation in the batch. + * + * @throws IllegalStateException If no operations have been added to the batch. + * @throws TableServiceException if any operation within the batch fails. See the documentation for the client + * methods in {@link TableClient} to understand the conditions that may cause a given operation to fail. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Response> submitTransactionWithResponse(List transactionalBatch, Duration timeout, Context context) { + return blockWithOptionalTimeout(client.submitTransactionWithResponse(transactionalBatch, context), timeout); + } } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java index ef9cda439ad69..c3a2da3e5b2f7 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java @@ -20,6 +20,7 @@ import com.azure.data.tables.implementation.StorageConnectionString; import com.azure.data.tables.implementation.StorageEndpoint; import com.azure.data.tables.implementation.TablesJacksonSerializer; +import com.azure.data.tables.implementation.TablesMultipartSerializer; import java.net.MalformedURLException; import java.net.URL; @@ -34,6 +35,7 @@ @ServiceClientBuilder(serviceClients = {TableClient.class, TableAsyncClient.class}) public final class TableClientBuilder { private static final SerializerAdapter TABLES_SERIALIZER = new TablesJacksonSerializer(); + private static final TablesMultipartSerializer BATCH_SERIALIZER = new TablesMultipartSerializer(); private final ClientLogger logger = new ClientLogger(TableClientBuilder.class); private final List perCallPolicies = new ArrayList<>(); @@ -85,7 +87,7 @@ public TableAsyncClient buildAsyncClient() { azureNamedKeyCredential, azureSasCredential, sasToken, endpoint, retryPolicy, httpLogOptions, clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, logger); - return new TableAsyncClient(tableName, pipeline, endpoint, serviceVersion, TABLES_SERIALIZER); + return new TableAsyncClient(tableName, pipeline, endpoint, serviceVersion, TABLES_SERIALIZER, BATCH_SERIALIZER); } /** diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java new file mode 100644 index 0000000000000..d01dc67272949 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables.models; + +/** + * Defines an transaction action to be included as part of a batch operation. + */ +public class TableTransactionAction { + private final TableTransactionActionType actionType; + private final TableEntity entity; + private final boolean ifUnchanged; + + /** + * Initializes a new instance of the {@link TableTransactionAction}. + * + * @param actionType The operation type to be applied to the {@code entity}. + * @param entity The table entity to which the {@code actionType} will be applied. + */ + public TableTransactionAction(TableTransactionActionType actionType, TableEntity entity) { + this(actionType, entity, false); + } + + /** + * Initializes a new instance of the {@link TableTransactionAction}. + * + * @param actionType The operation type to be applied to the {@code entity}. + * @param entity The table entity to which the {@code actionType} will be applied. + * @param ifUnchanged When {@code true}, the ETag of the provided entity must + * match the ETag of the entity in the Table service. If the values do not match, the update will not occur and + * an exception will be thrown. This value is only applied for update operations. + */ + public TableTransactionAction(TableTransactionActionType actionType, TableEntity entity, boolean ifUnchanged) { + this.actionType = actionType; + this.entity = entity; + this.ifUnchanged = ifUnchanged; + } + + /** + * Get the {@link TableTransactionActionType operation type} to be applied to the {@link TableEntity entity}. + * + * @return The {@link TableTransactionActionType operation type}. + */ + public TableTransactionActionType getActionType() { + return actionType; + } + + /** + * Get the {@link TableEntity table entity} to which the {@code actionType} will be applied. + * + * @return The {@link TableEntity table entity} to which the {@code actionType} will be applied + */ + public TableEntity getEntity() { + return entity; + } + + /** + * Get the {@code ifUnchanged} value of this action, which indicates if the ETag of the provided entity must + * match the ETag of the entity in the Table service. If the values do not match, the update will not occur and + * an exception will be thrown. This value is only applied for update operations. + * + * @return The {@code ifUnchanged} value of this action. + */ + public boolean getIfUnchanged() { + return ifUnchanged; + } +} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java new file mode 100644 index 0000000000000..17c00bae040e6 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables.models; + +import com.azure.data.tables.TableClient; + +/** + * The type of operation to be executed on a table entity as part of a table transactional batch of operations. + */ +public enum TableTransactionActionType { + /** + * Add the entity to the table. This is equivalent to {@link TableClient#createEntity(TableEntity)}. + */ + CREATE, + + /** + * Upsert the entity in {@link TableEntityUpdateMode#MERGE} mode. This is equivalent to + * {@link TableClient#upsertEntity(TableEntity)}. + */ + UPSERT_MERGE, + + /** + * Upsert the entity in {@link TableEntityUpdateMode#REPLACE} mode. This is equivalent to + * {@link TableClient#upsertEntity(TableEntity)}. + */ + UPSERT_REPLACE, + + /** + * Update the entity in {@link TableEntityUpdateMode#MERGE} mode. This is equivalent to + * {@link TableClient#updateEntity(TableEntity)}. + */ + UPDATE_MERGE, + + /** + * Update the entity in {@link TableEntityUpdateMode#REPLACE} mode. This is equivalent to + * {@link TableClient#updateEntity(TableEntity)}. + */ + UPDATE_REPLACE, + + /** + * Delete the entity. This is equivalent to {@link TableClient#deleteEntity(TableEntity)}. + */ + DELETE +} From 6faad40bb47c23d9e4e3d8548587a2bb95fdeb37 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Mon, 24 May 2021 17:51:49 -0500 Subject: [PATCH 02/16] Corrected the Exception type thrown by TableServiceAsyncClient.listTables() and TableAsyncClient.listEntitites() --- .../src/main/java/com/azure/data/tables/TableAsyncClient.java | 2 +- .../java/com/azure/data/tables/TableServiceAsyncClient.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index d2b24403dab2a..f1957217e9d0c 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -714,7 +714,7 @@ public PagedFlux listEntities(ListEntitiesOptions opt * @return A paged reactive result containing matching entities within the table. * * @throws IllegalArgumentException If one or more of the OData query options in {@code options} is malformed. - * @throws TableServiceErrorException If the request is rejected by the service. + * @throws TableServiceException If the request is rejected by the service. */ PagedFlux listEntities(ListEntitiesOptions options, Context context) { return new PagedFlux<>( diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableServiceAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableServiceAsyncClient.java index 236bcb3c6c89b..0eb9d2ff0e6d3 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableServiceAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableServiceAsyncClient.java @@ -333,7 +333,7 @@ public PagedFlux listTables(ListTablesOptions options) { * @return A paged reactive result containing matching tables within the account. * * @throws IllegalArgumentException If one or more of the OData query options in {@code options} is malformed. - * @throws TableServiceErrorException If the request is rejected by the service. + * @throws TableServiceException If the request is rejected by the service. */ PagedFlux listTables(ListTablesOptions options, Context context) { return new PagedFlux<>( From 36fa52a6653703ed10cded47e43948c473797198 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Mon, 24 May 2021 17:53:49 -0500 Subject: [PATCH 03/16] Removed TableAsyncBatch and TableBatch and re-recorded tests. --- .../azure/data/tables/TableAsyncBatch.java | 395 ------------------ .../azure/data/tables/TableAsyncClient.java | 23 - .../com/azure/data/tables/TableBatch.java | 270 ------------ .../com/azure/data/tables/TableClient.java | 18 - .../data/tables/TablesAsyncClientTest.java | 114 ++--- .../TablesAsyncClientTest.batchAsync.json | 77 ---- ...yncClientTest.batchAsyncAllOperations.json | 183 -------- ...cClientTest.batchImmutableAfterSubmit.json | 53 --- ...tTest.batchRequiresOperationsOnSubmit.json | 29 -- ...entTest.batchRequiresSamePartitionKey.json | 29 -- ...cClientTest.batchRequiresUniqueRowKey.json | 29 -- ...syncClientTest.submitTransactionAsync.json | 77 ++++ ...t.submitTransactionAsyncAllOperations.json | 183 ++++++++ ...TransactionAsyncWithFailingOperation.json} | 38 +- 14 files changed, 311 insertions(+), 1207 deletions(-) delete mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncBatch.java delete mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableBatch.java delete mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsync.json delete mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsyncAllOperations.json delete mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchImmutableAfterSubmit.json delete mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresOperationsOnSubmit.json delete mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresSamePartitionKey.json delete mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresUniqueRowKey.json create mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsync.json create mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncAllOperations.json rename sdk/tables/azure-data-tables/src/test/resources/session-records/{TablesAsyncClientTest.batchAsyncWithFailingOperation.json => TablesAsyncClientTest.submitTransactionAsyncWithFailingOperation.json} (53%) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncBatch.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncBatch.java deleted file mode 100644 index e22e3ef406298..0000000000000 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncBatch.java +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.data.tables; - -import com.azure.core.annotation.Fluent; -import com.azure.core.annotation.ReturnType; -import com.azure.core.annotation.ServiceMethod; -import com.azure.core.http.rest.Response; -import com.azure.core.http.rest.SimpleResponse; -import com.azure.core.util.Context; -import com.azure.core.util.CoreUtils; -import com.azure.core.util.logging.ClientLogger; -import com.azure.data.tables.implementation.BatchImpl; -import com.azure.data.tables.implementation.ModelHelper; -import com.azure.data.tables.implementation.TablesMultipartSerializer; -import com.azure.data.tables.implementation.models.BatchChangeSet; -import com.azure.data.tables.implementation.models.BatchOperation; -import com.azure.data.tables.implementation.models.BatchRequestBody; -import com.azure.data.tables.implementation.models.BatchSubRequest; -import com.azure.data.tables.implementation.models.BatchSubmitBatchResponse; -import com.azure.data.tables.implementation.models.TableServiceError; -import com.azure.data.tables.models.BatchOperationResponse; -import com.azure.data.tables.models.TableEntity; -import com.azure.data.tables.models.TableEntityUpdateMode; -import com.azure.data.tables.models.TableServiceException; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; - -import static com.azure.core.util.FluxUtil.monoError; -import static com.azure.core.util.FluxUtil.withContext; -import static com.azure.data.tables.implementation.TableUtils.toTableServiceError; - -/** - * Provides a batch object for asynchronously executing a transaction containing one or more operations on entities - * within a table in the Azure Tables service. - * - * The batch object represents a collection of one or more create, update, upsert, and/or delete operations on entities - * that share the same partition key within the table. When the batch is executed, all of the operations will be - * performed as part of a single transaction. As a result, either all operations in the batch will succeed, or if a - * failure occurs, all operations in the batch will be rolled back. Each operation in a batch must operate on a distinct - * row key. Attempting to add multiple operations to a batch that share the same row key will cause an exception to be - * thrown. - * - * Instances of this object are obtained by calling the {@link TableAsyncClient#createBatch(String)} method on a {@link - * TableAsyncClient} object. - */ -@Fluent -public final class TableAsyncBatch { - private static final TablesMultipartSerializer BATCH_SERIALIZER = new TablesMultipartSerializer(); - - private final ClientLogger logger = new ClientLogger(TableAsyncBatch.class); - private final String partitionKey; - private final TableAsyncClient operationClient; - private final BatchImpl batchImpl; - private final HashSet rowKeys = new HashSet<>(); - private final List operations = new ArrayList<>(); - private boolean frozen = false; - - TableAsyncBatch(String partitionKey, TableAsyncClient client) { - this.partitionKey = partitionKey; - this.batchImpl = new BatchImpl(client.getImplementation(), BATCH_SERIALIZER); - this.operationClient = new TableClientBuilder() - .tableName(client.getTableName()) - .endpoint(client.getImplementation().getUrl()) - .serviceVersion(client.getServiceVersion()) - .pipeline(BuilderHelper.buildNullClientPipeline()) - .buildAsyncClient(); - } - - /** - * Inserts an entity into the table. - * - * @param entity The entity to insert. - * - * @return The updated {@link TableAsyncBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableAsyncBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableAsyncBatch createEntity(TableEntity entity) { - validate(entity); - addOperation(new BatchOperation.CreateEntity(entity)); - - return this; - } - - /** - * Inserts an entity into the table if it does not exist, or merges the entity with the existing entity otherwise. - * - * If no entity exists within the table having the same partition key and row key as the provided entity, it will be - * inserted. Otherwise, the provided entity's properties will be merged into the existing entity. - * - * @param entity The entity to upsert. - * - * @return The updated {@link TableAsyncBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableAsyncBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableAsyncBatch upsertEntity(TableEntity entity) { - return upsertEntity(entity, TableEntityUpdateMode.MERGE); - } - - /** - * Inserts an entity into the table if it does not exist, or updates the existing entity using the specified update - * mode otherwise. - * - * If no entity exists within the table having the same partition key and row key as the provided entity, it will be - * inserted. Otherwise, the existing entity will be updated according to the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to upsert. - * @param updateMode The type of update to perform if the entity already exits. - * - * @return The updated {@link TableAsyncBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableAsyncBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableAsyncBatch upsertEntity(TableEntity entity, TableEntityUpdateMode updateMode) { - validate(entity); - addOperation(new BatchOperation.UpsertEntity(entity, updateMode)); - - return this; - } - - /** - * Updates an existing entity by merging the provided entity with the existing entity. - * - * @param entity The entity to update. - * - * @return The updated {@link TableAsyncBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableAsyncBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableAsyncBatch updateEntity(TableEntity entity) { - return updateEntity(entity, TableEntityUpdateMode.MERGE); - } - - /** - * Updates an existing entity using the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to update. - * @param updateMode The type of update to perform. - * - * @return The updated {@link TableAsyncBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableAsyncBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableAsyncBatch updateEntity(TableEntity entity, TableEntityUpdateMode updateMode) { - return updateEntity(entity, updateMode, false); - } - - /** - * Updates an existing entity using the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to update. - * @param updateMode The type of update to perform. - * @param ifUnchanged When true, the eTag of the provided entity must match the eTag of the entity in the Table - * service. If the values do not match, the update will not occur and an exception will be thrown. - * - * @return The updated {@link TableAsyncBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableAsyncBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableAsyncBatch updateEntity(TableEntity entity, TableEntityUpdateMode updateMode, boolean ifUnchanged) { - validate(entity); - addOperation(new BatchOperation.UpdateEntity(entity, updateMode, ifUnchanged)); - - return this; - } - - /** - * Deletes an entity from the table. - * - * @param rowKey The row key of the entity. - * - * @return The updated {@link TableAsyncBatch}. - * - * @throws IllegalArgumentException If the provided row key is {@code null} or empty, or if another operation with - * the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableAsyncBatch deleteEntity(String rowKey) { - return deleteEntity(rowKey, "*"); - } - - /** - * Deletes an entity from the table. - * - * @param rowKey The row key of the entity. - * @param eTag The value to compare with the eTag of the entity in the Tables service. If the values do not match, - * the delete will not occur and an exception will be thrown. - * - * @return The updated {@link TableAsyncBatch}. - * - * @throws IllegalArgumentException If the provided row key is {@code null} or empty, or if another operation with - * the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableAsyncBatch deleteEntity(String rowKey, String eTag) { - validate(partitionKey, rowKey); - addOperation(new BatchOperation.DeleteEntity(partitionKey, rowKey, eTag)); - - return this; - } - - /** - * Gets an immutable list containing all operations added to this batch. - * - * @return An immutable list containing all operations added to this batch. - */ - public synchronized List getOperations() { - return Collections.unmodifiableList(this.operations); - } - - /** - * Executes all operations within The batch inside a transaction. When the call completes, either all operations in - * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. - * - * @return A reactive result containing a list of sub-responses for each operation in the batch. - * - * @throws IllegalStateException If no operations have been added to the batch. - * @throws TableServiceException if any operation within the batch fails. See the documentation for the client - * methods in {@link TableAsyncClient} to understand the conditions that may cause a given operation to fail. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public synchronized Mono> submitTransaction() { - return submitTransactionWithResponse().flatMap(response -> Mono.justOrEmpty(response.getValue())); - } - - /** - * Executes all operations within The batch inside a transaction. When the call completes, either all operations in - * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. - * - * @return A reactive result containing the HTTP response produced for the batch itself. The response's value will - * contain a list of sub-responses for each operation in the batch. - * - * @throws IllegalStateException If no operations have been added to the batch. - * @throws TableServiceException if any operation within the batch fails. See the documentation for the client - * methods in {@link TableAsyncClient} to understand the conditions that may cause a given operation to fail. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public synchronized Mono>> submitTransactionWithResponse() { - return withContext(this::submitTransactionWithResponse); - } - - synchronized Mono>> submitTransactionWithResponse(Context context) { - this.frozen = true; - Context finalContext = context == null ? Context.NONE : context; - - if (operations.size() == 0) { - throw logger.logExceptionAsError(new IllegalStateException("A batch must contain at least one operation.")); - } - - return Flux.fromIterable(operations) - .flatMapSequential(op -> op.prepareRequest(operationClient).zipWith(Mono.just(op))) - .collect(BatchRequestBody::new, (body, pair) -> - body.addChangeOperation(new BatchSubRequest(pair.getT2(), pair.getT1()))) - .flatMap(body -> - batchImpl.submitBatchWithRestResponseAsync(body, null, finalContext).zipWith(Mono.just(body))) - .flatMap(pair -> parseResponse(pair.getT2(), pair.getT1())); - } - - private Mono>> parseResponse(BatchRequestBody requestBody, - BatchSubmitBatchResponse response) { - TableServiceError error = null; - String errorMessage = null; - BatchChangeSet changes = null; - BatchOperation failedOperation = null; - - if (requestBody.getContents().get(0) instanceof BatchChangeSet) { - changes = (BatchChangeSet) requestBody.getContents().get(0); - } - - for (int i = 0; i < response.getValue().length; i++) { - BatchOperationResponse subResponse = response.getValue()[i]; - - // Attempt to attach a sub-request to each batch sub-response - if (changes != null && changes.getContents().get(i) != null) { - ModelHelper.updateBatchOperationResponse(subResponse, - changes.getContents().get(i).getHttpRequest()); - } - - // If one sub-response was an error, we need to throw even though the service responded with 202 - if (subResponse.getStatusCode() >= 400 && error == null && errorMessage == null) { - if (subResponse.getValue() instanceof TableServiceError) { - error = (TableServiceError) subResponse.getValue(); - - // Make a best effort to locate the failed operation and include it in the message - if (changes != null && error.getOdataError() != null - && error.getOdataError().getMessage() != null - && error.getOdataError().getMessage().getValue() != null) { - - String message = error.getOdataError().getMessage().getValue(); - - try { - int failedIndex = Integer.parseInt(message.substring(0, message.indexOf(":"))); - failedOperation = changes.getContents().get(failedIndex).getOperation(); - } catch (NumberFormatException e) { - // Unable to parse failed operation from batch error message - this just means - // the service did not indicate which request was the one that failed. Since - // this is optional, just swallow the exception. - } - } - } else if (subResponse.getValue() instanceof String) { - errorMessage = "The service returned the following data for the failed operation: " - + subResponse.getValue(); - } else { - errorMessage = - "The service returned the following status code for the failed operation: " - + subResponse.getStatusCode(); - } - } - } - - if (error != null || errorMessage != null) { - String message = "An operation within the batch failed, the transaction has been rolled back."; - - if (failedOperation != null) { - message += " The failed operation was: " + failedOperation; - } else if (errorMessage != null) { - message += " " + errorMessage; - } - - return monoError(logger, new TableServiceException(message, null, toTableServiceError(error))); - } else { - return Mono.just(new SimpleResponse<>(response, Arrays.asList(response.getValue()))); - } - } - - private synchronized void addOperation(BatchOperation operation) { - operations.add(operation); - } - - private synchronized void validate(TableEntity entity) { - validate(entity.getPartitionKey(), entity.getRowKey()); - } - - private synchronized void validate(String partitionKey, String rowKey) { - if (this.frozen) { - throw logger.logExceptionAsError( - new IllegalStateException("Operations can't be modified once a batch is submitted.")); - } - - if (!this.partitionKey.equals(partitionKey)) { - throw logger.logExceptionAsError( - new IllegalArgumentException("All operations in a batch must share the same partition key.")); - } - - if (CoreUtils.isNullOrEmpty(rowKey)) { - throw logger.logExceptionAsError( - new IllegalArgumentException("The row key must not be null or empty.")); - } - - if (rowKeys.contains(rowKey)) { - throw logger.logExceptionAsError( - new IllegalArgumentException("Every operation in a batch must use a different row key.")); - } else { - rowKeys.add(rowKey); - } - } -} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index f1957217e9d0c..b79c533dd5dc2 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -192,29 +192,6 @@ public TableServiceVersion getServiceVersion() { return TableServiceVersion.fromString(tablesImplementation.getVersion()); } - /** - * Creates a new {@link TableAsyncBatch} object. Batch objects allow you to enqueue multiple create, update, upsert, - * and/or delete operations on entities that share the same partition key. When the batch is executed, all of the - * operations will be performed as part of a single transaction. As a result, either all operations in the batch - * will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation in a batch - * must operate on a distinct row key. Attempting to add multiple operations to a batch that share the same row key - * will cause an exception to be thrown. - * - * @param partitionKey The partition key shared by all operations in the batch. - * - * @return An object representing the batch, to which operations can be added. - * - * @throws IllegalArgumentException If the provided partition key is {@code null} or empty. - */ - public TableAsyncBatch createBatch(String partitionKey) { - if (isNullOrEmpty(partitionKey)) { - throw logger.logExceptionAsError( - new IllegalArgumentException("'partitionKey' cannot be null or empty.")); - } - - return new TableAsyncBatch(partitionKey, this); - } - /** * Creates the table within the Tables service. * diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableBatch.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableBatch.java deleted file mode 100644 index 7e7b8f11d83b9..0000000000000 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableBatch.java +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.data.tables; - -import com.azure.core.annotation.Fluent; -import com.azure.core.annotation.ReturnType; -import com.azure.core.annotation.ServiceMethod; -import com.azure.core.http.rest.Response; -import com.azure.core.util.Context; -import com.azure.data.tables.implementation.models.BatchOperation; -import com.azure.data.tables.models.BatchOperationResponse; -import com.azure.data.tables.models.TableEntity; -import com.azure.data.tables.models.TableEntityUpdateMode; -import com.azure.data.tables.models.TableServiceException; - -import java.time.Duration; -import java.util.List; - -import static com.azure.data.tables.implementation.TableUtils.blockWithOptionalTimeout; - -/** - * Provides a batch object for asynchronously executing a transaction containing one or more operations on entities - * within a table in the Azure Tables service. - * - * The batch object represents a collection of one or more create, update, upsert, and/or delete operations on entities - * that share the same partition key within the table. When the batch is executed, all of the operations will be - * performed as part of a single transaction. As a result, either all operations in the batch will succeed, or if a - * failure occurs, all operations in the batch will be rolled back. Each operation in a batch must operate on a distinct - * row key. Attempting to add multiple operations to a batch that share the same row key will cause an exception to be - * thrown. - * - * Instances of this object are obtained by calling the {@link TableClient#createBatch(String)} method on a - * {@link TableClient} object. - */ -@Fluent -public final class TableBatch { - private final TableAsyncBatch batch; - - TableBatch(TableAsyncBatch batch) { - this.batch = batch; - } - - /** - * Inserts an entity into the table. - * - * @param entity The entity to insert. - * - * @return The updated {@link TableBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableBatch createEntity(TableEntity entity) { - batch.createEntity(entity); - - return this; - } - - /** - * Inserts an entity into the table if it does not exist, or merges the entity with the existing entity otherwise. - * - * If no entity exists within the table having the same partition key and row key as the provided entity, it will be - * inserted. Otherwise, the provided entity's properties will be merged into the existing entity. - * - * @param entity The entity to upsert. - * - * @return The updated {@link TableBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableBatch upsertEntity(TableEntity entity) { - batch.upsertEntity(entity); - - return this; - } - - /** - * Inserts an entity into the table if it does not exist, or updates the existing entity using the specified update - * mode otherwise. - * - * If no entity exists within the table having the same partition key and row key as the provided entity, it will be - * inserted. Otherwise, the existing entity will be updated according to the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to upsert. - * @param updateMode The type of update to perform if the entity already exits. - * - * @return The updated {@link TableBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableBatch upsertEntity(TableEntity entity, TableEntityUpdateMode updateMode) { - batch.upsertEntity(entity, updateMode); - - return this; - } - - /** - * Updates an existing entity by merging the provided entity with the existing entity. - * - * @param entity The entity to update. - * - * @return The updated {@link TableBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableBatch updateEntity(TableEntity entity) { - batch.updateEntity(entity); - - return this; - } - - /** - * Updates an existing entity using the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to update. - * @param updateMode The type of update to perform. - * - * @return The updated {@link TableBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableBatch updateEntity(TableEntity entity, TableEntityUpdateMode updateMode) { - batch.updateEntity(entity, updateMode); - - return this; - } - - /** - * Updates an existing entity using the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to update. - * @param updateMode The type of update to perform. - * @param ifUnchanged When true, the eTag of the provided entity must match the eTag of the entity in the Table - * service. If the values do not match, the update will not occur and an exception will be thrown. - * - * @return The updated {@link TableBatch}. - * - * @throws IllegalArgumentException If the entity's partition key does not match the partition key provided when - * creating this {@link TableBatch} object, if the entity's row key is {@code null} or empty, or if another - * operation with the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableBatch updateEntity(TableEntity entity, TableEntityUpdateMode updateMode, boolean ifUnchanged) { - batch.updateEntity(entity, updateMode, ifUnchanged); - - return this; - } - - /** - * Deletes an entity from the table. - * - * @param rowKey The row key of the entity. - * - * @return The updated {@link TableBatch}. - * - * @throws IllegalArgumentException If the provided row key is {@code null} or empty, or if another operation with - * the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableBatch deleteEntity(String rowKey) { - batch.deleteEntity(rowKey); - - return this; - } - - /** - * Deletes an entity from the table. - * - * @param rowKey The row key of the entity. - * @param eTag The value to compare with the eTag of the entity in the Tables service. If the values do not match, - * the delete will not occur and an exception will be thrown. - * - * @return The updated {@link TableBatch}. - * - * @throws IllegalArgumentException If the provided row key is {@code null} or empty, or if another operation with - * the same row key has already been added to the batch. - * @throws IllegalStateException If this method is called after the batch has been submitted. - */ - public TableBatch deleteEntity(String rowKey, String eTag) { - batch.deleteEntity(rowKey, eTag); - - return this; - } - - /** - * Gets an immutable list containing all operations added to this batch. - * - * @return An immutable list containing all operations added to this batch. - */ - public List getOperations() { - return batch.getOperations(); - } - - /** - * Executes all operations within The batch inside a transaction. When the call completes, either all operations in - * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. - * - * @return A list of sub-responses for each operation in the batch. - * - * @throws IllegalStateException If no operations have been added to the batch. - * @throws TableServiceException if any operation within the batch fails. See the documentation for the client - * methods in {@link TableClient} to understand the conditions that may cause a given operation to fail. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public List submitTransaction() { - return batch.submitTransaction().block(); - } - - /** - * Executes all operations within The batch inside a transaction. When the call completes, either all operations in - * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. - * - * @param timeout Duration to wait for the operation to complete. - * - * @return A list of sub-responses for each operation in the batch. - * - * @throws IllegalStateException If no operations have been added to the batch. - * @throws TableServiceException if any operation within the batch fails. See the documentation for the client - * methods in {@link TableClient} to understand the conditions that may cause a given operation to fail. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public List submitTransaction(Duration timeout) { - return blockWithOptionalTimeout(batch.submitTransaction(), timeout); - } - - /** - * Executes all operations within The batch inside a transaction. When the call completes, either all operations in - * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. - * - * @param timeout Duration to wait for the operation to complete. - * @param context Additional context that is passed through the HTTP pipeline during the service call. - * - * @return The HTTP response produced for the batch itself. The response's value will contain a list of - * sub-responses for each operation in the batch. - * - * @throws IllegalStateException If no operations have been added to the batch. - * @throws TableServiceException if any operation within the batch fails. See the documentation for the client - * methods in {@link TableClient} to understand the conditions that may cause a given operation to fail. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public Response> submitTransactionWithResponse(Duration timeout, Context context) { - return blockWithOptionalTimeout(batch.submitTransactionWithResponse(context), timeout); - } -} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java index 911ea2b5e398c..cb73dca634536 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java @@ -78,24 +78,6 @@ public TableServiceVersion getServiceVersion() { return this.client.getServiceVersion(); } - /** - * Creates a new {@link TableBatch} object. Batch objects allow you to enqueue multiple create, update, upsert, - * and/or delete operations on entities that share the same partition key. When the batch is executed, all of the - * operations will be performed as part of a single transaction. As a result, either all operations in the batch - * will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation in a batch - * must operate on a distinct row key. Attempting to add multiple operations to a batch that share the same row key - * will cause an exception to be thrown. - * - * @param partitionKey The partition key shared by all operations in the batch. - * - * @return An object representing the batch, to which operations can be added. - * - * @throws IllegalArgumentException If the provided partition key is {@code null} or empty. - */ - public TableBatch createBatch(String partitionKey) { - return new TableBatch(this.client.createBatch(partitionKey)); - } - /** * Creates the table within the Tables service. * diff --git a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java index 9faff7bfe8be8..d1fc454593775 100644 --- a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java +++ b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java @@ -17,6 +17,8 @@ import com.azure.data.tables.models.TableEntity; import com.azure.data.tables.models.TableEntityUpdateMode; import com.azure.data.tables.models.TableServiceException; +import com.azure.data.tables.models.TableTransactionAction; +import com.azure.data.tables.models.TableTransactionActionType; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -35,7 +37,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -715,23 +716,26 @@ void listEntitiesSubclassAsync() { @Test @Tag("Batch") - void batchAsync() { + void submitTransactionAsync() { String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); String rowKeyValue = testResourceNamer.randomName("rowKey", 20); String rowKeyValue2 = testResourceNamer.randomName("rowKey", 20); int expectedBatchStatusCode = 202; int expectedOperationStatusCode = 204; - TableAsyncBatch batch = tableClient.createBatch(partitionKeyValue); - batch.createEntity(new TableEntity(partitionKeyValue, rowKeyValue)) - .createEntity(new TableEntity(partitionKeyValue, rowKeyValue2)); + List transactionalBatch = new ArrayList<>(); + transactionalBatch.add(new TableTransactionAction( + TableTransactionActionType.CREATE, new TableEntity(partitionKeyValue, rowKeyValue))); + transactionalBatch.add(new TableTransactionAction( + TableTransactionActionType.CREATE, new TableEntity(partitionKeyValue, rowKeyValue2))); // Act & Assert - final Response> result = batch.submitTransactionWithResponse().block(TIMEOUT); + final Response> result = + tableClient.submitTransactionWithResponse(transactionalBatch).block(TIMEOUT); assertNotNull(result); assertEquals(expectedBatchStatusCode, result.getStatusCode()); - assertEquals(batch.getOperations().size(), result.getValue().size()); + assertEquals(transactionalBatch.size(), result.getValue().size()); assertEquals(expectedOperationStatusCode, result.getValue().get(0).getStatusCode()); assertEquals(expectedOperationStatusCode, result.getValue().get(1).getStatusCode()); @@ -752,7 +756,7 @@ void batchAsync() { @Test @Tag("Batch") - void batchAsyncAllOperations() { + void submitTransactionAsyncAllOperations() { String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); String rowKeyValueCreate = testResourceNamer.randomName("rowKey", 20); String rowKeyValueUpsertInsert = testResourceNamer.randomName("rowKey", 20); @@ -771,7 +775,6 @@ void batchAsyncAllOperations() { tableClient.createEntity(new TableEntity(partitionKeyValue, rowKeyValueUpdateReplace)).block(TIMEOUT); tableClient.createEntity(new TableEntity(partitionKeyValue, rowKeyValueDelete)).block(TIMEOUT); - TableEntity toUpsertMerge = new TableEntity(partitionKeyValue, rowKeyValueUpsertMerge); toUpsertMerge.addProperty("Test", "MergedValue"); @@ -784,22 +787,25 @@ void batchAsyncAllOperations() { TableEntity toUpdateReplace = new TableEntity(partitionKeyValue, rowKeyValueUpdateReplace); toUpdateReplace.addProperty("Test", "MergedValue"); - TableAsyncBatch batch = tableClient.createBatch(partitionKeyValue); - batch.createEntity(new TableEntity(partitionKeyValue, rowKeyValueCreate)) - .upsertEntity(new TableEntity(partitionKeyValue, rowKeyValueUpsertInsert)) - .upsertEntity(toUpsertMerge, TableEntityUpdateMode.MERGE) - .upsertEntity(toUpsertReplace, TableEntityUpdateMode.REPLACE) - .updateEntity(toUpdateMerge, TableEntityUpdateMode.MERGE) - .updateEntity(toUpdateReplace, TableEntityUpdateMode.REPLACE) - .deleteEntity(rowKeyValueDelete); + List transactionalBatch = new ArrayList<>(); + transactionalBatch.add(new TableTransactionAction(TableTransactionActionType.CREATE, + new TableEntity(partitionKeyValue, rowKeyValueCreate))); + transactionalBatch.add(new TableTransactionAction(TableTransactionActionType.UPSERT_MERGE, + new TableEntity(partitionKeyValue, rowKeyValueUpsertInsert))); + transactionalBatch.add(new TableTransactionAction(TableTransactionActionType.UPSERT_MERGE, toUpsertMerge)); + transactionalBatch.add(new TableTransactionAction(TableTransactionActionType.UPSERT_REPLACE, toUpsertReplace)); + transactionalBatch.add(new TableTransactionAction(TableTransactionActionType.UPDATE_MERGE, toUpdateMerge)); + transactionalBatch.add(new TableTransactionAction(TableTransactionActionType.UPDATE_REPLACE, toUpdateReplace)); + transactionalBatch.add(new TableTransactionAction(TableTransactionActionType.DELETE, + new TableEntity(partitionKeyValue, rowKeyValueDelete))); // Act & Assert - StepVerifier.create(batch.submitTransactionWithResponse()) + StepVerifier.create(tableClient.submitTransactionWithResponse(transactionalBatch)) .assertNext(response -> { assertNotNull(response); assertEquals(expectedBatchStatusCode, response.getStatusCode()); List subResponses = response.getValue(); - assertEquals(batch.getOperations().size(), subResponses.size()); + assertEquals(transactionalBatch.size(), subResponses.size()); for (BatchOperationResponse subResponse : subResponses) { assertEquals(expectedOperationStatusCode, subResponse.getStatusCode()); } @@ -810,17 +816,19 @@ void batchAsyncAllOperations() { @Test @Tag("Batch") - void batchAsyncWithFailingOperation() { + void submitTransactionAsyncWithFailingOperation() { String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); String rowKeyValue = testResourceNamer.randomName("rowKey", 20); String rowKeyValue2 = testResourceNamer.randomName("rowKey", 20); - TableAsyncBatch batch = tableClient.createBatch(partitionKeyValue); - batch.createEntity(new TableEntity(partitionKeyValue, rowKeyValue)) - .deleteEntity(rowKeyValue2); + List transactionalBatch = new ArrayList<>(); + transactionalBatch.add(new TableTransactionAction(TableTransactionActionType.CREATE, + new TableEntity(partitionKeyValue, rowKeyValue))); + transactionalBatch.add(new TableTransactionAction(TableTransactionActionType.DELETE, + new TableEntity(partitionKeyValue, rowKeyValue2))); // Act & Assert - StepVerifier.create(batch.submitTransactionWithResponse()) + StepVerifier.create(tableClient.submitTransactionWithResponse(transactionalBatch)) .expectErrorMatches(e -> e instanceof TableServiceException && e.getMessage().contains("An operation within the batch failed") && e.getMessage().contains("The failed operation was") @@ -829,62 +837,4 @@ void batchAsyncWithFailingOperation() { && e.getMessage().contains("rowKey='" + rowKeyValue2)) .verify(); } - - @Test - @Tag("Batch") - void batchRequiresSamePartitionKey() { - String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); - String partitionKeyValue2 = testResourceNamer.randomName("partitionKey", 20); - String rowKeyValue = testResourceNamer.randomName("rowKey", 20); - - TableAsyncBatch batch = tableClient.createBatch(partitionKeyValue); - - // Act & Assert - assertThrows(IllegalArgumentException.class, - () -> batch.createEntity(new TableEntity(partitionKeyValue2, rowKeyValue))); - } - - @Test - @Tag("Batch") - void batchRequiresUniqueRowKey() { - String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); - String rowKeyValue = testResourceNamer.randomName("rowKey", 20); - - TableAsyncBatch batch = tableClient.createBatch(partitionKeyValue); - batch.createEntity(new TableEntity(partitionKeyValue, rowKeyValue)); - - // Act & Assert - assertThrows(IllegalArgumentException.class, - () -> batch.createEntity(new TableEntity(partitionKeyValue, rowKeyValue))); - } - - @Test - @Tag("Batch") - void batchRequiresOperationsOnSubmit() { - String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); - - TableAsyncBatch batch = tableClient.createBatch(partitionKeyValue); - - // Act & Assert - StepVerifier.create(batch.submitTransaction()) - .expectError(IllegalStateException.class) - .verify(); - } - - @Test - @Tag("Batch") - void batchImmutableAfterSubmit() { - String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); - String rowKeyValue = testResourceNamer.randomName("rowKey", 20); - String rowKeyValue2 = testResourceNamer.randomName("rowKey", 20); - - TableAsyncBatch batch = tableClient.createBatch(partitionKeyValue); - batch.createEntity(new TableEntity(partitionKeyValue, rowKeyValue)); - batch.submitTransaction().block(TIMEOUT); - - // Act & Assert - assertThrows(IllegalStateException.class, - () -> batch.createEntity(new TableEntity(partitionKeyValue, rowKeyValue2))); - } - } diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsync.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsync.json deleted file mode 100644 index 1f4d50c4cf29a..0000000000000 --- a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsync.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "networkCallRecords" : [ { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/Tables", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "a5d6b7cc-5b4c-4610-9491-4ec9110f432e", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:14:03 GMT", - "Cache-Control" : "no-cache", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename11504502')", - "Content-Length" : "0", - "x-ms-request-id" : "6c9ccc0d-4002-0003-39e2-36960a000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "a5d6b7cc-5b4c-4610-9491-4ec9110f432e", - "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename11504502')" - }, - "Exception" : null - }, { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/$batch", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "3ebe404a-2aad-41b6-8619-c5f0b30a3a42", - "Content-Type" : "multipart/mixed; boundary=batch_5186181f-c9be-48e3-ad90-02de70965718" - }, - "Response" : { - "Transfer-Encoding" : "chunked", - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "Cache-Control" : "no-cache", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "202", - "x-ms-request-id" : "6c9ccc26-4002-0003-4fe2-36960a000000", - "Body" : "--batchresponse_66c8e64d-a54c-43fa-b147-78332a2384b6\r\nContent-Type: multipart/mixed; boundary=changesetresponse_9348fc83-be0c-44ed-92bc-b2bdf471d707\r\n\r\n--changesetresponse_9348fc83-be0c-44ed-92bc-b2bdf471d707\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nPreference-Applied: return-no-content\r\nDataServiceVersion: 3.0;\r\nLocation: https://tablesstoragetests.table.core.windows.net/tablename11504502(PartitionKey='partitionkey663032',RowKey='rowkey45676c5cf')\r\nDataServiceId: https://tablesstoragetests.table.core.windows.net/tablename11504502(PartitionKey='partitionkey663032',RowKey='rowkey45676c5cf')\r\nETag: W/\"datetime'2021-04-21T19%3A14%3A03.9375897Z'\"\r\n\r\n\r\n--changesetresponse_9348fc83-be0c-44ed-92bc-b2bdf471d707\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nPreference-Applied: return-no-content\r\nDataServiceVersion: 3.0;\r\nLocation: https://tablesstoragetests.table.core.windows.net/tablename11504502(PartitionKey='partitionkey663032',RowKey='rowkey417310a7b')\r\nDataServiceId: https://tablesstoragetests.table.core.windows.net/tablename11504502(PartitionKey='partitionkey663032',RowKey='rowkey417310a7b')\r\nETag: W/\"datetime'2021-04-21T19%3A14%3A03.9375897Z'\"\r\n\r\n\r\n--changesetresponse_9348fc83-be0c-44ed-92bc-b2bdf471d707--\r\n--batchresponse_66c8e64d-a54c-43fa-b147-78332a2384b6--\r\n", - "Date" : "Wed, 21 Apr 2021 19:14:03 GMT", - "x-ms-client-request-id" : "3ebe404a-2aad-41b6-8619-c5f0b30a3a42", - "Content-Type" : "multipart/mixed; boundary=batchresponse_66c8e64d-a54c-43fa-b147-78332a2384b6" - }, - "Exception" : null - }, { - "Method" : "GET", - "Uri" : "https://REDACTED.table.core.windows.net/tablename11504502(PartitionKey='partitionkey663032',RowKey='rowkey45676c5cf')?$format=application/json%3Bodata%3Dfullmetadata", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "8bbe8a38-8b52-4f1d-a2ac-a9d1984be918" - }, - "Response" : { - "Transfer-Encoding" : "chunked", - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "200", - "Date" : "Wed, 21 Apr 2021 19:14:03 GMT", - "Cache-Control" : "no-cache", - "ETag" : "W/datetime'2021-04-21T19%3A14%3A03.9375897Z'", - "x-ms-request-id" : "6c9ccc2d-4002-0003-56e2-36960a000000", - "Body" : "{\"odata.metadata\":\"https://tablesstoragetests.table.core.windows.net/$metadata#tablename11504502/@Element\",\"odata.type\":\"tablesstoragetests.tablename11504502\",\"odata.id\":\"https://tablesstoragetests.table.core.windows.net/tablename11504502(PartitionKey='partitionkey663032',RowKey='rowkey45676c5cf')\",\"odata.etag\":\"W/\\\"datetime'2021-04-21T19%3A14%3A03.9375897Z'\\\"\",\"odata.editLink\":\"tablename11504502(PartitionKey='partitionkey663032',RowKey='rowkey45676c5cf')\",\"PartitionKey\":\"partitionkey663032\",\"RowKey\":\"rowkey45676c5cf\",\"Timestamp@odata.type\":\"Edm.DateTime\",\"Timestamp\":\"2021-04-21T19:14:03.9375897Z\"}", - "x-ms-client-request-id" : "8bbe8a38-8b52-4f1d-a2ac-a9d1984be918", - "Content-Type" : "application/json;odata=fullmetadata;streaming=true;charset=utf-8" - }, - "Exception" : null - } ], - "variables" : [ "tablename11504502", "partitionkey663032", "rowkey45676c5cf", "rowkey417310a7b" ] -} diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsyncAllOperations.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsyncAllOperations.json deleted file mode 100644 index 0efaed681e6a7..0000000000000 --- a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsyncAllOperations.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "networkCallRecords" : [ { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/Tables", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "3e208f55-6638-4f36-be8c-a4b27af1aef1", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:14:13 GMT", - "Cache-Control" : "no-cache", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename60735cca')", - "Content-Length" : "0", - "x-ms-request-id" : "139543f9-7002-0066-5ae2-362757000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "3e208f55-6638-4f36-be8c-a4b27af1aef1", - "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename60735cca')" - }, - "Exception" : null - }, { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/tablename60735cca", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "b265a4ea-91b9-4285-8e26-04b38ad90f71", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:14:13 GMT", - "Cache-Control" : "no-cache", - "ETag" : "W/datetime'2021-04-21T19%3A14%3A14.8499929Z'", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey484597833')", - "Content-Length" : "0", - "x-ms-request-id" : "13954401-7002-0066-60e2-362757000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "b265a4ea-91b9-4285-8e26-04b38ad90f71", - "Location" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey484597833')" - }, - "Exception" : null - }, { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/tablename60735cca", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "20e2a101-e90f-4b70-bfe7-80f34375a95c", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:14:13 GMT", - "Cache-Control" : "no-cache", - "ETag" : "W/datetime'2021-04-21T19%3A14%3A14.8810152Z'", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey59136e0ff')", - "Content-Length" : "0", - "x-ms-request-id" : "13954403-7002-0066-62e2-362757000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "20e2a101-e90f-4b70-bfe7-80f34375a95c", - "Location" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey59136e0ff')" - }, - "Exception" : null - }, { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/tablename60735cca", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "e33c3954-f813-4718-bc84-f1d39295ee1e", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:14:13 GMT", - "Cache-Control" : "no-cache", - "ETag" : "W/datetime'2021-04-21T19%3A14%3A14.9090343Z'", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey20670aec3')", - "Content-Length" : "0", - "x-ms-request-id" : "13954409-7002-0066-68e2-362757000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "e33c3954-f813-4718-bc84-f1d39295ee1e", - "Location" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey20670aec3')" - }, - "Exception" : null - }, { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/tablename60735cca", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "0a6b0391-36fb-4203-adb1-211beba94037", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:14:14 GMT", - "Cache-Control" : "no-cache", - "ETag" : "W/datetime'2021-04-21T19%3A14%3A14.9360538Z'", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey44870a98d')", - "Content-Length" : "0", - "x-ms-request-id" : "1395440d-7002-0066-6ce2-362757000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "0a6b0391-36fb-4203-adb1-211beba94037", - "Location" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey44870a98d')" - }, - "Exception" : null - }, { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/tablename60735cca", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "46a4f4e2-ba6a-4742-925f-de232fda56fb", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:14:14 GMT", - "Cache-Control" : "no-cache", - "ETag" : "W/datetime'2021-04-21T19%3A14%3A14.9640738Z'", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey412042daf')", - "Content-Length" : "0", - "x-ms-request-id" : "1395440f-7002-0066-6ee2-362757000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "46a4f4e2-ba6a-4742-925f-de232fda56fb", - "Location" : "https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey412042daf')" - }, - "Exception" : null - }, { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/$batch", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "0c88d0cc-5f28-4842-a62e-9b212c2ea8ed", - "Content-Type" : "multipart/mixed; boundary=batch_109e2e4d-44f5-4a40-884a-93ccbb82d78c" - }, - "Response" : { - "Transfer-Encoding" : "chunked", - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "Cache-Control" : "no-cache", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "202", - "x-ms-request-id" : "1395441a-7002-0066-79e2-362757000000", - "Body" : "--batchresponse_eeac2668-de44-4010-bb7a-cd08b32f64fa\r\nContent-Type: multipart/mixed; boundary=changesetresponse_81cc8aeb-20ec-4ecb-9066-a9bdef57393d\r\n\r\n--changesetresponse_81cc8aeb-20ec-4ecb-9066-a9bdef57393d\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nPreference-Applied: return-no-content\r\nDataServiceVersion: 3.0;\r\nLocation: https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey975345ed9')\r\nDataServiceId: https://tablesstoragetests.table.core.windows.net/tablename60735cca(PartitionKey='partitionkey95940d',RowKey='rowkey975345ed9')\r\nETag: W/\"datetime'2021-04-21T19%3A14%3A15.0371236Z'\"\r\n\r\n\r\n--changesetresponse_81cc8aeb-20ec-4ecb-9066-a9bdef57393d\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-04-21T19%3A14%3A15.0375753Z'\"\r\n\r\n\r\n--changesetresponse_81cc8aeb-20ec-4ecb-9066-a9bdef57393d\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-04-21T19%3A14%3A15.0375753Z'\"\r\n\r\n\r\n--changesetresponse_81cc8aeb-20ec-4ecb-9066-a9bdef57393d\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-04-21T19%3A14%3A15.0375753Z'\"\r\n\r\n\r\n--changesetresponse_81cc8aeb-20ec-4ecb-9066-a9bdef57393d\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-04-21T19%3A14%3A15.0375753Z'\"\r\n\r\n\r\n--changesetresponse_81cc8aeb-20ec-4ecb-9066-a9bdef57393d\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-04-21T19%3A14%3A15.0375753Z'\"\r\n\r\n\r\n--changesetresponse_81cc8aeb-20ec-4ecb-9066-a9bdef57393d\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\n\r\n\r\n--changesetresponse_81cc8aeb-20ec-4ecb-9066-a9bdef57393d--\r\n--batchresponse_eeac2668-de44-4010-bb7a-cd08b32f64fa--\r\n", - "Date" : "Wed, 21 Apr 2021 19:14:14 GMT", - "x-ms-client-request-id" : "0c88d0cc-5f28-4842-a62e-9b212c2ea8ed", - "Content-Type" : "multipart/mixed; boundary=batchresponse_eeac2668-de44-4010-bb7a-cd08b32f64fa" - }, - "Exception" : null - } ], - "variables" : [ "tablename60735cca", "partitionkey95940d", "rowkey975345ed9", "rowkey043600faf", "rowkey484597833", "rowkey59136e0ff", "rowkey20670aec3", "rowkey44870a98d", "rowkey412042daf" ] -} diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchImmutableAfterSubmit.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchImmutableAfterSubmit.json deleted file mode 100644 index e238bc5bbd578..0000000000000 --- a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchImmutableAfterSubmit.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "networkCallRecords" : [ { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/Tables", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "0244355f-bcac-4d4a-82b3-31e214e99ea7", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:13:54 GMT", - "Cache-Control" : "no-cache", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename16858d5d')", - "Content-Length" : "0", - "x-ms-request-id" : "dd5526e2-b002-009e-3fe2-36ec4a000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "0244355f-bcac-4d4a-82b3-31e214e99ea7", - "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename16858d5d')" - }, - "Exception" : null - }, { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/$batch", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "08b0f776-e25f-45f8-836f-1cdd938a32c8", - "Content-Type" : "multipart/mixed; boundary=batch_0d9bd997-141d-45c4-8878-a01f4e4d94bf" - }, - "Response" : { - "Transfer-Encoding" : "chunked", - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "Cache-Control" : "no-cache", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "202", - "x-ms-request-id" : "dd5526e8-b002-009e-43e2-36ec4a000000", - "Body" : "--batchresponse_0ecafb21-128e-422f-8951-8b6f65a364ab\r\nContent-Type: multipart/mixed; boundary=changesetresponse_5caeaeaf-a17b-4650-8b3e-8acb70b168d2\r\n\r\n--changesetresponse_5caeaeaf-a17b-4650-8b3e-8acb70b168d2\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nPreference-Applied: return-no-content\r\nDataServiceVersion: 3.0;\r\nLocation: https://tablesstoragetests.table.core.windows.net/tablename16858d5d(PartitionKey='partitionkey93352a',RowKey='rowkey4692844bf')\r\nDataServiceId: https://tablesstoragetests.table.core.windows.net/tablename16858d5d(PartitionKey='partitionkey93352a',RowKey='rowkey4692844bf')\r\nETag: W/\"datetime'2021-04-21T19%3A13%3A55.6895343Z'\"\r\n\r\n\r\n--changesetresponse_5caeaeaf-a17b-4650-8b3e-8acb70b168d2--\r\n--batchresponse_0ecafb21-128e-422f-8951-8b6f65a364ab--\r\n", - "Date" : "Wed, 21 Apr 2021 19:13:54 GMT", - "x-ms-client-request-id" : "08b0f776-e25f-45f8-836f-1cdd938a32c8", - "Content-Type" : "multipart/mixed; boundary=batchresponse_0ecafb21-128e-422f-8951-8b6f65a364ab" - }, - "Exception" : null - } ], - "variables" : [ "tablename16858d5d", "partitionkey93352a", "rowkey4692844bf", "rowkey751799898" ] -} diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresOperationsOnSubmit.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresOperationsOnSubmit.json deleted file mode 100644 index e4ae9b6505566..0000000000000 --- a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresOperationsOnSubmit.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "networkCallRecords" : [ { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/Tables", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "1aa3db9f-0906-435b-9e81-c5998e422de0", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:14:19 GMT", - "Cache-Control" : "no-cache", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename19986f70')", - "Content-Length" : "0", - "x-ms-request-id" : "279f4c88-f002-005e-6ce2-36660e000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "1aa3db9f-0906-435b-9e81-c5998e422de0", - "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename19986f70')" - }, - "Exception" : null - } ], - "variables" : [ "tablename19986f70", "partitionkey669455" ] -} diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresSamePartitionKey.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresSamePartitionKey.json deleted file mode 100644 index 7d61b16caa331..0000000000000 --- a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresSamePartitionKey.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "networkCallRecords" : [ { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/Tables", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "78537e71-5c77-41f6-8ea5-73ed13270e0a", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:14:37 GMT", - "Cache-Control" : "no-cache", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename23297ac8')", - "Content-Length" : "0", - "x-ms-request-id" : "e0c21339-2002-0018-5ee2-36b898000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "78537e71-5c77-41f6-8ea5-73ed13270e0a", - "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename23297ac8')" - }, - "Exception" : null - } ], - "variables" : [ "tablename23297ac8", "partitionkey69478c", "partitionkey01585c", "rowkey087573841" ] -} diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresUniqueRowKey.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresUniqueRowKey.json deleted file mode 100644 index 10c8a40d350da..0000000000000 --- a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchRequiresUniqueRowKey.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "networkCallRecords" : [ { - "Method" : "POST", - "Uri" : "https://REDACTED.table.core.windows.net/Tables", - "Headers" : { - "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "5d9da31a-adba-482d-bf33-f1d02f32a676", - "Content-Type" : "application/json;odata=nometadata" - }, - "Response" : { - "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", - "X-Content-Type-Options" : "nosniff", - "retry-after" : "0", - "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:13:33 GMT", - "Cache-Control" : "no-cache", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename093700b8')", - "Content-Length" : "0", - "x-ms-request-id" : "a11823f2-a002-0020-5ae2-36f9c1000000", - "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "5d9da31a-adba-482d-bf33-f1d02f32a676", - "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename093700b8')" - }, - "Exception" : null - } ], - "variables" : [ "tablename093700b8", "partitionkey80794d", "rowkey012841ff0" ] -} diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsync.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsync.json new file mode 100644 index 0000000000000..df5cd7be4cfdf --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsync.json @@ -0,0 +1,77 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/Tables", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "363b1d88-0da8-47d6-9425-a0c3fde88bdc", + "Content-Type" : "application/json;odata=nometadata" + }, + "Response" : { + "content-length" : "0", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "204", + "Date" : "Mon, 24 May 2021 22:08:41 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename945097e4')", + "x-ms-request-id" : "a3847db1-9002-003d-3fe9-506b47000000", + "x-ms-client-request-id" : "363b1d88-0da8-47d6-9425-a0c3fde88bdc", + "Preference-Applied" : "return-no-content", + "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename945097e4')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/$batch", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "88768dbb-45cf-4d0b-8576-b8e7a7e71313", + "Content-Type" : "multipart/mixed; boundary=batch_cce1f765-7364-4447-a6eb-dcf3c5b06b48" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2019-02-02", + "Cache-Control" : "no-cache", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "a3847e27-9002-003d-2ee9-506b47000000", + "Body" : "--batchresponse_b1e62115-6462-4096-9905-e5a902abc898\r\nContent-Type: multipart/mixed; boundary=changesetresponse_67a2594f-7680-4515-8d94-e5c9a061ddc5\r\n\r\n--changesetresponse_67a2594f-7680-4515-8d94-e5c9a061ddc5\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nPreference-Applied: return-no-content\r\nDataServiceVersion: 3.0;\r\nLocation: https://tablesstoragetests.table.core.windows.net/tablename945097e4(PartitionKey='partitionkey520656',RowKey='rowkey348724835')\r\nDataServiceId: https://tablesstoragetests.table.core.windows.net/tablename945097e4(PartitionKey='partitionkey520656',RowKey='rowkey348724835')\r\nETag: W/\"datetime'2021-05-24T22%3A08%3A41.6700649Z'\"\r\n\r\n\r\n--changesetresponse_67a2594f-7680-4515-8d94-e5c9a061ddc5\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nPreference-Applied: return-no-content\r\nDataServiceVersion: 3.0;\r\nLocation: https://tablesstoragetests.table.core.windows.net/tablename945097e4(PartitionKey='partitionkey520656',RowKey='rowkey14494478d')\r\nDataServiceId: https://tablesstoragetests.table.core.windows.net/tablename945097e4(PartitionKey='partitionkey520656',RowKey='rowkey14494478d')\r\nETag: W/\"datetime'2021-05-24T22%3A08%3A41.6700649Z'\"\r\n\r\n\r\n--changesetresponse_67a2594f-7680-4515-8d94-e5c9a061ddc5--\r\n--batchresponse_b1e62115-6462-4096-9905-e5a902abc898--\r\n", + "x-ms-client-request-id" : "88768dbb-45cf-4d0b-8576-b8e7a7e71313", + "Date" : "Mon, 24 May 2021 22:08:41 GMT", + "Content-Type" : "multipart/mixed; boundary=batchresponse_b1e62115-6462-4096-9905-e5a902abc898" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.table.core.windows.net/tablename945097e4(PartitionKey='partitionkey520656',RowKey='rowkey348724835')?$format=application/json%3Bodata%3Dfullmetadata", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "dfadb872-5aae-47e4-9e49-2c40d4a6fcc1" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "200", + "Date" : "Mon, 24 May 2021 22:08:41 GMT", + "Cache-Control" : "no-cache", + "eTag" : "W/datetime'2021-05-24T22%3A08%3A41.6700649Z'", + "x-ms-request-id" : "a3847e7e-9002-003d-01e9-506b47000000", + "Body" : "{\"odata.metadata\":\"https://tablesstoragetests.table.core.windows.net/$metadata#tablename945097e4/@Element\",\"odata.type\":\"tablesstoragetests.tablename945097e4\",\"odata.id\":\"https://tablesstoragetests.table.core.windows.net/tablename945097e4(PartitionKey='partitionkey520656',RowKey='rowkey348724835')\",\"odata.etag\":\"W/\\\"datetime'2021-05-24T22%3A08%3A41.6700649Z'\\\"\",\"odata.editLink\":\"tablename945097e4(PartitionKey='partitionkey520656',RowKey='rowkey348724835')\",\"PartitionKey\":\"partitionkey520656\",\"RowKey\":\"rowkey348724835\",\"Timestamp@odata.type\":\"Edm.DateTime\",\"Timestamp\":\"2021-05-24T22:08:41.6700649Z\"}", + "x-ms-client-request-id" : "dfadb872-5aae-47e4-9e49-2c40d4a6fcc1", + "Content-Type" : "application/json;odata=fullmetadata;streaming=true;charset=utf-8" + }, + "Exception" : null + } ], + "variables" : [ "tablename945097e4", "partitionkey520656", "rowkey348724835", "rowkey14494478d" ] +} \ No newline at end of file diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncAllOperations.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncAllOperations.json new file mode 100644 index 0000000000000..58d09b37f91db --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncAllOperations.json @@ -0,0 +1,183 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/Tables", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "462f1825-3ab1-4c23-a37c-258f2a01b677", + "Content-Type" : "application/json;odata=nometadata" + }, + "Response" : { + "content-length" : "0", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "204", + "Date" : "Mon, 24 May 2021 22:24:40 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename886663e4')", + "x-ms-request-id" : "add1e014-7002-0037-0eeb-5072ce000000", + "x-ms-client-request-id" : "462f1825-3ab1-4c23-a37c-258f2a01b677", + "Preference-Applied" : "return-no-content", + "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename886663e4')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename886663e4", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "747e7702-dcf0-4f29-8408-4c52eaad2bd0", + "Content-Type" : "application/json;odata=nometadata" + }, + "Response" : { + "content-length" : "0", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "204", + "Date" : "Mon, 24 May 2021 22:24:40 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey27349c87b')", + "eTag" : "W/datetime'2021-05-24T22%3A24%3A41.8049083Z'", + "x-ms-request-id" : "add1e072-7002-0037-67eb-5072ce000000", + "x-ms-client-request-id" : "747e7702-dcf0-4f29-8408-4c52eaad2bd0", + "Preference-Applied" : "return-no-content", + "Location" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey27349c87b')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename886663e4", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "fe37237f-4721-4410-a030-47b8355d917c", + "Content-Type" : "application/json;odata=nometadata" + }, + "Response" : { + "content-length" : "0", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "204", + "Date" : "Mon, 24 May 2021 22:24:41 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey4122739ec')", + "eTag" : "W/datetime'2021-05-24T22%3A24%3A41.8879666Z'", + "x-ms-request-id" : "add1e0ad-7002-0037-22eb-5072ce000000", + "x-ms-client-request-id" : "fe37237f-4721-4410-a030-47b8355d917c", + "Preference-Applied" : "return-no-content", + "Location" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey4122739ec')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename886663e4", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "6ed4708b-6747-4f02-8700-534485a989c1", + "Content-Type" : "application/json;odata=nometadata" + }, + "Response" : { + "content-length" : "0", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "204", + "Date" : "Mon, 24 May 2021 22:24:41 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey640834945')", + "eTag" : "W/datetime'2021-05-24T22%3A24%3A41.9660225Z'", + "x-ms-request-id" : "add1e0d7-7002-0037-4ceb-5072ce000000", + "x-ms-client-request-id" : "6ed4708b-6747-4f02-8700-534485a989c1", + "Preference-Applied" : "return-no-content", + "Location" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey640834945')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename886663e4", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "abc07b5b-c892-4432-921f-357e43235cf2", + "Content-Type" : "application/json;odata=nometadata" + }, + "Response" : { + "content-length" : "0", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "204", + "Date" : "Mon, 24 May 2021 22:24:41 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey39447c7fb')", + "eTag" : "W/datetime'2021-05-24T22%3A24%3A42.053084Z'", + "x-ms-request-id" : "add1e0f6-7002-0037-6aeb-5072ce000000", + "x-ms-client-request-id" : "abc07b5b-c892-4432-921f-357e43235cf2", + "Preference-Applied" : "return-no-content", + "Location" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey39447c7fb')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename886663e4", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "24c0781b-e3bd-4b70-a07f-163a41256fab", + "Content-Type" : "application/json;odata=nometadata" + }, + "Response" : { + "content-length" : "0", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "204", + "Date" : "Mon, 24 May 2021 22:24:41 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey35625193a')", + "eTag" : "W/datetime'2021-05-24T22%3A24%3A42.1331395Z'", + "x-ms-request-id" : "add1e111-7002-0037-05eb-5072ce000000", + "x-ms-client-request-id" : "24c0781b-e3bd-4b70-a07f-163a41256fab", + "Preference-Applied" : "return-no-content", + "Location" : "https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey35625193a')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/$batch", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "6c06cbd9-27d5-42b0-ac94-4f638ff5ebc7", + "Content-Type" : "multipart/mixed; boundary=batch_ade22be0-4a35-4979-9761-71cd33a6c081" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2019-02-02", + "Cache-Control" : "no-cache", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "add1e15f-7002-0037-51eb-5072ce000000", + "Body" : "--batchresponse_47ac2270-16ef-47d6-a48a-77e6c36b4d89\r\nContent-Type: multipart/mixed; boundary=changesetresponse_2a143db4-ab0c-4e27-9ffd-d510619cffcc\r\n\r\n--changesetresponse_2a143db4-ab0c-4e27-9ffd-d510619cffcc\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nPreference-Applied: return-no-content\r\nDataServiceVersion: 3.0;\r\nLocation: https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey27338f277')\r\nDataServiceId: https://tablesstoragetests.table.core.windows.net/tablename886663e4(PartitionKey='partitionkey48210f',RowKey='rowkey27338f277')\r\nETag: W/\"datetime'2021-05-24T22%3A24%3A42.295254Z'\"\r\n\r\n\r\n--changesetresponse_2a143db4-ab0c-4e27-9ffd-d510619cffcc\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-05-24T22%3A24%3A42.2960448Z'\"\r\n\r\n\r\n--changesetresponse_2a143db4-ab0c-4e27-9ffd-d510619cffcc\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-05-24T22%3A24%3A42.2960448Z'\"\r\n\r\n\r\n--changesetresponse_2a143db4-ab0c-4e27-9ffd-d510619cffcc\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-05-24T22%3A24%3A42.2960448Z'\"\r\n\r\n\r\n--changesetresponse_2a143db4-ab0c-4e27-9ffd-d510619cffcc\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-05-24T22%3A24%3A42.2960448Z'\"\r\n\r\n\r\n--changesetresponse_2a143db4-ab0c-4e27-9ffd-d510619cffcc\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\nETag: W/\"datetime'2021-05-24T22%3A24%3A42.2960448Z'\"\r\n\r\n\r\n--changesetresponse_2a143db4-ab0c-4e27-9ffd-d510619cffcc\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 204 No Content\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 1.0;\r\n\r\n\r\n--changesetresponse_2a143db4-ab0c-4e27-9ffd-d510619cffcc--\r\n--batchresponse_47ac2270-16ef-47d6-a48a-77e6c36b4d89--\r\n", + "x-ms-client-request-id" : "6c06cbd9-27d5-42b0-ac94-4f638ff5ebc7", + "Date" : "Mon, 24 May 2021 22:24:41 GMT", + "Content-Type" : "multipart/mixed; boundary=batchresponse_47ac2270-16ef-47d6-a48a-77e6c36b4d89" + }, + "Exception" : null + } ], + "variables" : [ "tablename886663e4", "partitionkey48210f", "rowkey27338f277", "rowkey220964732", "rowkey27349c87b", "rowkey4122739ec", "rowkey640834945", "rowkey39447c7fb", "rowkey35625193a" ] +} \ No newline at end of file diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsyncWithFailingOperation.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithFailingOperation.json similarity index 53% rename from sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsyncWithFailingOperation.json rename to sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithFailingOperation.json index 17e12d3ae2a0c..34cf883b9c0b4 100644 --- a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.batchAsyncWithFailingOperation.json +++ b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithFailingOperation.json @@ -4,24 +4,24 @@ "Uri" : "https://REDACTED.table.core.windows.net/Tables", "Headers" : { "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "683480f8-e7ad-4fad-87ce-aaa7eaa3c074", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "8a68175c-cac7-40c0-96ea-d23fdf7e45ba", "Content-Type" : "application/json;odata=nometadata" }, "Response" : { + "content-length" : "0", "x-ms-version" : "2019-02-02", "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", "X-Content-Type-Options" : "nosniff", "retry-after" : "0", "StatusCode" : "204", - "Date" : "Wed, 21 Apr 2021 19:13:28 GMT", + "Date" : "Mon, 24 May 2021 22:28:23 GMT", "Cache-Control" : "no-cache", - "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename160226e5')", - "Content-Length" : "0", - "x-ms-request-id" : "59c01e2a-d002-00a7-01e2-36acee000000", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename0112287f')", + "x-ms-request-id" : "45af7d0c-3002-0012-24ec-50ea7d000000", + "x-ms-client-request-id" : "8a68175c-cac7-40c0-96ea-d23fdf7e45ba", "Preference-Applied" : "return-no-content", - "x-ms-client-request-id" : "683480f8-e7ad-4fad-87ce-aaa7eaa3c074", - "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename160226e5')" + "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename0112287f')" }, "Exception" : null }, { @@ -29,25 +29,25 @@ "Uri" : "https://REDACTED.table.core.windows.net/$batch", "Headers" : { "x-ms-version" : "2019-02-02", - "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.7 (11.0.6; Windows 10; 10.0)", - "x-ms-client-request-id" : "3d1711b1-717c-4f48-ab9a-fe93d2577cdb", - "Content-Type" : "multipart/mixed; boundary=batch_15c14867-7614-4da7-98f0-15af3865b36a" + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "f30d8b24-c52b-4467-a9bf-77fa1a1a200d", + "Content-Type" : "multipart/mixed; boundary=batch_73744653-0eba-416e-870a-5db05eb558fb" }, "Response" : { "Transfer-Encoding" : "chunked", "x-ms-version" : "2019-02-02", - "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", "Cache-Control" : "no-cache", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", "X-Content-Type-Options" : "nosniff", "retry-after" : "0", "StatusCode" : "202", - "x-ms-request-id" : "59c01e3d-d002-00a7-0fe2-36acee000000", - "Body" : "--batchresponse_f24b0c83-88a5-4b49-a8df-2d8fc9ad4e28\r\nContent-Type: multipart/mixed; boundary=changesetresponse_b802b199-1107-4d3c-addc-05402308a98c\r\n\r\n--changesetresponse_b802b199-1107-4d3c-addc-05402308a98c\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 404 Not Found\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 3.0;\r\nContent-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8\r\n\r\n{\"odata.error\":{\"code\":\"ResourceNotFound\",\"message\":{\"lang\":\"en-US\",\"value\":\"1:The specified resource does not exist.\\nRequestId:59c01e3d-d002-00a7-0fe2-36acee000000\\nTime:2021-04-21T19:13:29.0819808Z\"}}}\r\n--changesetresponse_b802b199-1107-4d3c-addc-05402308a98c--\r\n--batchresponse_f24b0c83-88a5-4b49-a8df-2d8fc9ad4e28--\r\n", - "Date" : "Wed, 21 Apr 2021 19:13:28 GMT", - "x-ms-client-request-id" : "3d1711b1-717c-4f48-ab9a-fe93d2577cdb", - "Content-Type" : "multipart/mixed; boundary=batchresponse_f24b0c83-88a5-4b49-a8df-2d8fc9ad4e28" + "x-ms-request-id" : "45af7d32-3002-0012-45ec-50ea7d000000", + "Body" : "--batchresponse_0cf6609c-0d29-46eb-8c7d-d69a279b7d5d\r\nContent-Type: multipart/mixed; boundary=changesetresponse_4db7810c-12e9-4ac3-9519-0feb1ba180a5\r\n\r\n--changesetresponse_4db7810c-12e9-4ac3-9519-0feb1ba180a5\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 404 Not Found\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nDataServiceVersion: 3.0;\r\nContent-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8\r\n\r\n{\"odata.error\":{\"code\":\"ResourceNotFound\",\"message\":{\"lang\":\"en-US\",\"value\":\"1:The specified resource does not exist.\\nRequestId:45af7d32-3002-0012-45ec-50ea7d000000\\nTime:2021-05-24T22:28:24.4583558Z\"}}}\r\n--changesetresponse_4db7810c-12e9-4ac3-9519-0feb1ba180a5--\r\n--batchresponse_0cf6609c-0d29-46eb-8c7d-d69a279b7d5d--\r\n", + "x-ms-client-request-id" : "f30d8b24-c52b-4467-a9bf-77fa1a1a200d", + "Date" : "Mon, 24 May 2021 22:28:23 GMT", + "Content-Type" : "multipart/mixed; boundary=batchresponse_0cf6609c-0d29-46eb-8c7d-d69a279b7d5d" }, "Exception" : null } ], - "variables" : [ "tablename160226e5", "partitionkey71181c", "rowkey4379817c3", "rowkey582912dea" ] -} + "variables" : [ "tablename0112287f", "partitionkey836246", "rowkey80173c839", "rowkey323355ba6" ] +} \ No newline at end of file From e703727a97de18befb8504a8728a142537baf5fe Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Mon, 24 May 2021 18:14:46 -0500 Subject: [PATCH 04/16] Fixed SpotBugs issue. --- .../src/main/java/com/azure/data/tables/TableAsyncClient.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index b79c533dd5dc2..4d14438e7436c 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -1162,6 +1162,8 @@ Mono>> submitTransactionWithResponse(List< operations.add(new BatchOperation.DeleteEntity(transactionAction.getEntity().getPartitionKey(), transactionAction.getEntity().getRowKey(), transactionAction.getEntity().getETag())); + break; + default: break; } } From 74a98b33867ae49fd1cec2d6c49481e101341ebf Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Tue, 25 May 2021 18:35:56 -0500 Subject: [PATCH 05/16] Changed batch-related class names from Batch* to TransactionalBatch*. Intoduced a new exception for transactional batch operations : TablesTransactionFailedException. Introduced a new return type containing all sub-responses from submitting a transactional batch of actions: TableTransactionResult. --- .../azure/data/tables/TableAsyncClient.java | 147 ++++++++++-------- .../com/azure/data/tables/TableClient.java | 53 ++++--- .../azure/data/tables/TableClientBuilder.java | 5 +- .../data/tables/implementation/BatchImpl.java | 83 ---------- .../tables/implementation/ModelHelper.java | 85 +++++----- .../tables/implementation/SasProtocol.java | 1 - .../TablesMultipartSerializer.java | 49 +++--- .../TransactionalBatchImpl.java | 88 +++++++++++ .../models/BatchSubmitBatchHeaders.java | 123 --------------- .../models/BatchSubmitBatchResponse.java | 37 ----- .../implementation/models/MultipartPart.java | 3 +- .../models/TableServiceErrorOdataError.java | 1 - .../TableServiceErrorOdataErrorMessage.java | 1 - ...ion.java => TransactionalBatchAction.java} | 11 +- ....java => TransactionalBatchChangeSet.java} | 4 +- ...ava => TransactionalBatchRequestBody.java} | 25 +-- .../models/TransactionalBatchResponse.java | 39 +++++ ...java => TransactionalBatchSubRequest.java} | 8 +- .../TransactionalBatchSubmitBatchHeaders.java | 123 +++++++++++++++ .../tables/models/TableServiceException.java | 2 +- .../tables/models/TableTransactionAction.java | 2 +- ...va => TableTransactionActionResponse.java} | 18 +-- .../models/TableTransactionActionType.java | 2 +- .../TableTransactionFailedException.java | 52 +++++++ .../tables/models/TableTransactionResult.java | 65 ++++++++ .../data/tables/TablesAsyncClientTest.java | 22 +-- 26 files changed, 602 insertions(+), 447 deletions(-) delete mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/BatchImpl.java create mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/TransactionalBatchImpl.java delete mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubmitBatchHeaders.java delete mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubmitBatchResponse.java rename sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/{BatchOperation.java => TransactionalBatchAction.java} (93%) rename sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/{BatchChangeSet.java => TransactionalBatchChangeSet.java} (62%) rename sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/{BatchRequestBody.java => TransactionalBatchRequestBody.java} (50%) create mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchResponse.java rename sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/{BatchSubRequest.java => TransactionalBatchSubRequest.java} (62%) create mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchSubmitBatchHeaders.java rename sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/{BatchOperationResponse.java => TableTransactionActionResponse.java} (66%) create mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionFailedException.java create mode 100644 sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionResult.java diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index 4d14438e7436c..ac1ad19051c86 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -21,15 +21,15 @@ import com.azure.core.util.serializer.SerializerAdapter; import com.azure.data.tables.implementation.AzureTableImpl; import com.azure.data.tables.implementation.AzureTableImplBuilder; -import com.azure.data.tables.implementation.BatchImpl; +import com.azure.data.tables.implementation.TransactionalBatchImpl; import com.azure.data.tables.implementation.ModelHelper; import com.azure.data.tables.implementation.TableUtils; import com.azure.data.tables.implementation.models.AccessPolicy; -import com.azure.data.tables.implementation.models.BatchChangeSet; -import com.azure.data.tables.implementation.models.BatchOperation; -import com.azure.data.tables.implementation.models.BatchRequestBody; -import com.azure.data.tables.implementation.models.BatchSubRequest; -import com.azure.data.tables.implementation.models.BatchSubmitBatchResponse; +import com.azure.data.tables.implementation.models.TransactionalBatchChangeSet; +import com.azure.data.tables.implementation.models.TransactionalBatchAction; +import com.azure.data.tables.implementation.models.TransactionalBatchRequestBody; +import com.azure.data.tables.implementation.models.TransactionalBatchSubRequest; +import com.azure.data.tables.implementation.models.TransactionalBatchResponse; import com.azure.data.tables.implementation.models.OdataMetadataFormat; import com.azure.data.tables.implementation.models.QueryOptions; import com.azure.data.tables.implementation.models.ResponseFormat; @@ -38,7 +38,7 @@ import com.azure.data.tables.implementation.models.TableProperties; import com.azure.data.tables.implementation.models.TableResponseProperties; import com.azure.data.tables.implementation.models.TableServiceError; -import com.azure.data.tables.models.BatchOperationResponse; +import com.azure.data.tables.models.TableTransactionActionResponse; import com.azure.data.tables.models.ListEntitiesOptions; import com.azure.data.tables.models.TableAccessPolicy; import com.azure.data.tables.models.TableEntity; @@ -47,6 +47,8 @@ import com.azure.data.tables.models.TableServiceException; import com.azure.data.tables.models.TableSignedIdentifier; import com.azure.data.tables.models.TableTransactionAction; +import com.azure.data.tables.models.TableTransactionFailedException; +import com.azure.data.tables.models.TableTransactionResult; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -83,14 +85,14 @@ public final class TableAsyncClient { private final ClientLogger logger = new ClientLogger(TableAsyncClient.class); private final String tableName; private final AzureTableImpl tablesImplementation; - private final BatchImpl batchImplementation; + private final TransactionalBatchImpl transactionalBatchImplementation; private final String accountName; private final String tableEndpoint; private final HttpPipeline pipeline; - private final TableAsyncClient batchPrepClient; + private final TableAsyncClient transactionalBatchClient; TableAsyncClient(String tableName, HttpPipeline pipeline, String serviceUrl, TableServiceVersion serviceVersion, - SerializerAdapter tablesSerializer, SerializerAdapter batchSerializer) { + SerializerAdapter tablesSerializer, SerializerAdapter transactionalBatchSerializer) { try { if (tableName == null) { throw new NullPointerException("'tableName' must not be null to create a TableClient."); @@ -115,13 +117,13 @@ public final class TableAsyncClient { .pipeline(pipeline) .version(serviceVersion.getVersion()) .buildClient(); - this.batchImplementation = new BatchImpl(tablesImplementation, batchSerializer); + this.transactionalBatchImplementation = new TransactionalBatchImpl(tablesImplementation, transactionalBatchSerializer); this.tableName = tableName; this.pipeline = tablesImplementation.getHttpPipeline(); - this.batchPrepClient = new TableAsyncClient(this, serviceVersion, tablesSerializer); + this.transactionalBatchClient = new TableAsyncClient(this, serviceVersion, tablesSerializer); } - // Create a hollow client to be used for obtaining the body of a batch operation to submit. + // Create a hollow client to be used for obtaining the body of a transaction operation to submit. TableAsyncClient(TableAsyncClient client, ServiceVersion serviceVersion, SerializerAdapter tablesSerializer) { this.accountName = client.getAccountName(); this.tableEndpoint = client.getTableEndpoint(); @@ -134,8 +136,8 @@ public final class TableAsyncClient { .buildClient(); this.tableName = client.getTableName(); // A batch prep client does not need its own batch prep client nor batch implementation. - this.batchImplementation = null; - this.batchPrepClient = null; + this.transactionalBatchImplementation = null; + this.transactionalBatchClient = null; } /** @@ -1081,85 +1083,89 @@ private AccessPolicy toAccessPolicy(TableAccessPolicy tableAccessPolicy) { } /** - * Executes all operations within the batch inside a transaction. When the call completes, either all operations in - * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation - * in a batch must operate on a distinct row key. Attempting to add multiple operations to a batch that share the - * same row key will cause an error. + * Executes all operations within the list inside a transaction. When the call completes, either all operations in + * the transaction will succeed, or if a failure occurs, all operations in the transaction will be rolled back. + * Each operation must operate on a distinct row key. Attempting to pass multiple operations that share the same + * row key will cause an error. * - * @param transactionalBatch A list of {@link TableTransactionAction transaction actions} to perform on entities + * @param transactionActions A list of {@link TableTransactionAction transaction actions} to perform on entities * in a table. * - * @return A reactive result containing a list of {@link BatchOperationResponse sub-responses} for each operation - * in the batch. + * @return A reactive result containing a list of {@link TableTransactionActionResponse sub-responses} that + * correspond to each operation in the transaction * - * @throws IllegalStateException If no operations have been added to the batch. - * @throws TableServiceException If any operation within the batch fails. See the documentation for other client - * methods in {@link TableAsyncClient} to understand the conditions that may cause a given operation to fail. + * @throws IllegalStateException If no operations have been added to the list. + * @throws TableTransactionFailedException if any operation within the transaction fails. See the documentation + * for the client methods in {@link TableClient} to understand the conditions that may cause a given operation to + * fail. */ @ServiceMethod(returns = ReturnType.SINGLE) - public synchronized Mono> submitTransaction(List transactionalBatch) { - return submitTransactionWithResponse(transactionalBatch) + public synchronized Mono submitTransaction(List transactionActions) { + return submitTransactionWithResponse(transactionActions) .flatMap(response -> Mono.justOrEmpty(response.getValue())); } /** - * Executes all operations within the batch inside a transaction. When the call completes, either all operations in - * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation - * in a batch must operate on a distinct row key. Attempting to add multiple operations to a batch that share the - * same row key will cause an error. + * Executes all operations within the list inside a transaction. When the call completes, either all operations in + * the transaction will succeed, or if a failure occurs, all operations in the transaction will be rolled back. + * Each operation must operate on a distinct row key. Attempting to pass multiple operations that share the same + * row key will cause an error. * - * @param transactionalBatch A list of {@link TableTransactionAction transaction actions} to perform on entities + * @param transactionActions A list of {@link TableTransactionAction transaction actions} to perform on entities * in a table. * - * @return A reactive result containing the HTTP response produced for the batch itself. The response's value will - * contain a list of {@link BatchOperationResponse sub-responses} for each operation in the batch. + * @return A reactive result containing the HTTP response produced for the transaction itself. The response's + * value will contain a list of {@link TableTransactionActionResponse sub-responses} that correspond to each + * operation in the transaction. * - * @throws IllegalStateException If no operations have been added to the batch. - * @throws TableServiceException if any operation within the batch fails. See the documentation for the client - * methods in {@link TableAsyncClient} to understand the conditions that may cause a given operation to fail. + * @throws IllegalStateException If no operations have been added to the list. + * @throws TableTransactionFailedException if any operation within the transaction fails. See the documentation + * for the client methods in {@link TableClient} to understand the conditions that may cause a given operation to + * fail. */ @ServiceMethod(returns = ReturnType.SINGLE) - public synchronized Mono>> submitTransactionWithResponse(List transactionalBatch) { - return withContext(context -> submitTransactionWithResponse(transactionalBatch, context)); + public synchronized Mono> submitTransactionWithResponse(List transactionActions) { + return withContext(context -> submitTransactionWithResponse(transactionActions, context)); } - Mono>> submitTransactionWithResponse(List transactionalBatch, Context context) { + Mono> submitTransactionWithResponse(List transactionActions, Context context) { Context finalContext = context == null ? Context.NONE : context; - if (transactionalBatch.size() == 0) { - throw logger.logExceptionAsError(new IllegalStateException("A batch must contain at least one operation.")); + if (transactionActions.size() == 0) { + throw logger.logExceptionAsError( + new IllegalStateException("A transaction must contain at least one operation.")); } - final List operations = new ArrayList<>(); + final List operations = new ArrayList<>(); - for (TableTransactionAction transactionAction : transactionalBatch) { + for (TableTransactionAction transactionAction : transactionActions) { switch (transactionAction.getActionType()) { case CREATE: - operations.add(new BatchOperation.CreateEntity(transactionAction.getEntity())); + operations.add(new TransactionalBatchAction.CreateEntity(transactionAction.getEntity())); break; case UPSERT_MERGE: - operations.add(new BatchOperation.UpsertEntity(transactionAction.getEntity(), + operations.add(new TransactionalBatchAction.UpsertEntity(transactionAction.getEntity(), TableEntityUpdateMode.MERGE)); break; case UPSERT_REPLACE: - operations.add(new BatchOperation.UpsertEntity(transactionAction.getEntity(), + operations.add(new TransactionalBatchAction.UpsertEntity(transactionAction.getEntity(), TableEntityUpdateMode.REPLACE)); break; case UPDATE_MERGE: - operations.add(new BatchOperation.UpdateEntity(transactionAction.getEntity(), + operations.add(new TransactionalBatchAction.UpdateEntity(transactionAction.getEntity(), TableEntityUpdateMode.MERGE, transactionAction.getIfUnchanged())); break; case UPDATE_REPLACE: - operations.add(new BatchOperation.UpdateEntity(transactionAction.getEntity(), + operations.add(new TransactionalBatchAction.UpdateEntity(transactionAction.getEntity(), TableEntityUpdateMode.REPLACE, transactionAction.getIfUnchanged())); break; case DELETE: - operations.add(new BatchOperation.DeleteEntity(transactionAction.getEntity().getPartitionKey(), + operations.add(new TransactionalBatchAction.DeleteEntity(transactionAction.getEntity().getPartitionKey(), transactionAction.getEntity().getRowKey(), transactionAction.getEntity().getETag())); break; @@ -1169,31 +1175,35 @@ Mono>> submitTransactionWithResponse(List< } return Flux.fromIterable(operations) - .flatMapSequential(op -> op.prepareRequest(batchPrepClient).zipWith(Mono.just(op))) - .collect(BatchRequestBody::new, (body, pair) -> - body.addChangeOperation(new BatchSubRequest(pair.getT2(), pair.getT1()))) + .flatMapSequential(op -> op.prepareRequest(transactionalBatchClient).zipWith(Mono.just(op))) + .collect(TransactionalBatchRequestBody::new, (body, pair) -> + body.addChangeOperation(new TransactionalBatchSubRequest(pair.getT2(), pair.getT1()))) .flatMap(body -> - batchImplementation.submitBatchWithRestResponseAsync(body, null, finalContext).zipWith(Mono.just(body))) - .flatMap(pair -> parseResponse(pair.getT2(), pair.getT1())); + transactionalBatchImplementation.submitTransactionalBatchWithRestResponseAsync(body, null, finalContext).zipWith(Mono.just(body))) + .flatMap(pair -> parseResponse(pair.getT2(), pair.getT1())) + .map(response -> + new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), + new TableTransactionResult(transactionActions, response.getValue()))); } - private Mono>> parseResponse(BatchRequestBody requestBody, - BatchSubmitBatchResponse response) { + private Mono>> parseResponse(TransactionalBatchRequestBody requestBody, + TransactionalBatchResponse response) { TableServiceError error = null; String errorMessage = null; - BatchChangeSet changes = null; - BatchOperation failedOperation = null; + TransactionalBatchChangeSet changes = null; + TransactionalBatchAction failedAction = null; + Integer failedIndex = null; - if (requestBody.getContents().get(0) instanceof BatchChangeSet) { - changes = (BatchChangeSet) requestBody.getContents().get(0); + if (requestBody.getContents().get(0) instanceof TransactionalBatchChangeSet) { + changes = (TransactionalBatchChangeSet) requestBody.getContents().get(0); } for (int i = 0; i < response.getValue().length; i++) { - BatchOperationResponse subResponse = response.getValue()[i]; + TableTransactionActionResponse subResponse = response.getValue()[i]; // Attempt to attach a sub-request to each batch sub-response if (changes != null && changes.getContents().get(i) != null) { - ModelHelper.updateBatchOperationResponse(subResponse, + ModelHelper.updateTableTransactionActionResponse(subResponse, changes.getContents().get(i).getHttpRequest()); } @@ -1210,8 +1220,8 @@ private Mono>> parseResponse(BatchRequestB String message = error.getOdataError().getMessage().getValue(); try { - int failedIndex = Integer.parseInt(message.substring(0, message.indexOf(":"))); - failedOperation = changes.getContents().get(failedIndex).getOperation(); + failedIndex = Integer.parseInt(message.substring(0, message.indexOf(":"))); + failedAction = changes.getContents().get(failedIndex).getOperation(); } catch (NumberFormatException e) { // Unable to parse failed operation from batch error message - this just means // the service did not indicate which request was the one that failed. Since @@ -1232,13 +1242,14 @@ private Mono>> parseResponse(BatchRequestB if (error != null || errorMessage != null) { String message = "An operation within the batch failed, the transaction has been rolled back."; - if (failedOperation != null) { - message += " The failed operation was: " + failedOperation; + if (failedAction != null) { + message += " The failed operation was: " + failedAction; } else if (errorMessage != null) { message += " " + errorMessage; } - return monoError(logger, new TableServiceException(message, null, toTableServiceError(error))); + return monoError(logger, + new TableTransactionFailedException(message, null, toTableServiceError(error), failedIndex)); } else { return Mono.just(new SimpleResponse<>(response, Arrays.asList(response.getValue()))); } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java index cb73dca634536..e756a0681b388 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java @@ -8,7 +8,7 @@ import com.azure.core.http.rest.PagedIterable; import com.azure.core.http.rest.Response; import com.azure.core.util.Context; -import com.azure.data.tables.models.BatchOperationResponse; +import com.azure.data.tables.models.TableTransactionActionResponse; import com.azure.data.tables.models.ListEntitiesOptions; import com.azure.data.tables.models.TableEntity; import com.azure.data.tables.models.TableEntityUpdateMode; @@ -16,6 +16,8 @@ import com.azure.data.tables.models.TableServiceException; import com.azure.data.tables.models.TableSignedIdentifier; import com.azure.data.tables.models.TableTransactionAction; +import com.azure.data.tables.models.TableTransactionFailedException; +import com.azure.data.tables.models.TableTransactionResult; import java.time.Duration; import java.util.List; @@ -506,45 +508,48 @@ public Response setAccessPolicyWithResponse(List ta } /** - * Executes all operations within the batch inside a transaction. When the call completes, either all operations in - * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation - * in a batch must operate on a distinct row key. Attempting to add multiple operations to a batch that share the - * same row key will cause an error. + * Executes all operations within the list inside a transaction. When the call completes, either all operations in + * the transaction will succeed, or if a failure occurs, all operations in the transaction will be rolled back. + * Each operation must operate on a distinct row key. Attempting to pass multiple operations that share the same + * row key will cause an error. * - * @param transactionalBatch A list of {@link TableTransactionAction transaction actions} to perform on entities + * @param transactionActions A list of {@link TableTransactionAction transaction actions} to perform on entities * in a table. * - * @return A list of {@link BatchOperationResponse sub-responses} for each operation in the batch. + * @return A list of {@link TableTransactionActionResponse sub-responses} that correspond to each operation in the + * transaction. * - * @throws IllegalStateException If no operations have been added to the batch. - * @throws TableServiceException if any operation within the batch fails. See the documentation for the client - * methods in {@link TableClient} to understand the conditions that may cause a given operation to fail. + * @throws IllegalStateException If no operations have been added to the list. + * @throws TableTransactionFailedException if any operation within the transaction fails. See the documentation + * for the client methods in {@link TableClient} to understand the conditions that may cause a given operation to + * fail. */ @ServiceMethod(returns = ReturnType.SINGLE) - public List submitTransaction(List transactionalBatch) { - return client.submitTransaction(transactionalBatch).block(); + public TableTransactionResult submitTransaction(List transactionActions) { + return client.submitTransaction(transactionActions).block(); } /** - * Executes all operations within the batch inside a transaction. When the call completes, either all operations in - * the batch will succeed, or if a failure occurs, all operations in the batch will be rolled back. Each operation - * in a batch must operate on a distinct row key. Attempting to add multiple operations to a batch that share the - * same row key will cause an error. + * Executes all operations within the list inside a transaction. When the call completes, either all operations in + * the transaction will succeed, or if a failure occurs, all operations in the transaction will be rolled back. + * Each operation must operate on a distinct row key. Attempting to pass multiple operations that share the same + * row key will cause an error. * - * @param transactionalBatch A list of {@link TableTransactionAction transaction actions} to perform on entities + * @param transactionActions A list of {@link TableTransactionAction transaction actions} to perform on entities * in a table. * @param timeout Duration to wait for the operation to complete. * @param context Additional context that is passed through the HTTP pipeline during the service call. * - * @return An HTTP response produced for the batch itself. The response's value will contain a list of - * {@link BatchOperationResponse sub-responses} for each operation in the batch. + * @return An HTTP response produced for the transaction itself. The response's value will contain a list of + * {@link TableTransactionActionResponse sub-responses} that correspond to each operation in the transaction. * - * @throws IllegalStateException If no operations have been added to the batch. - * @throws TableServiceException if any operation within the batch fails. See the documentation for the client - * methods in {@link TableClient} to understand the conditions that may cause a given operation to fail. + * @throws IllegalStateException If no operations have been added to the list. + * @throws TableTransactionFailedException if any operation within the transaction fails. See the documentation + * for the client methods in {@link TableClient} to understand the conditions that may cause a given operation to + * fail. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Response> submitTransactionWithResponse(List transactionalBatch, Duration timeout, Context context) { - return blockWithOptionalTimeout(client.submitTransactionWithResponse(transactionalBatch, context), timeout); + public Response submitTransactionWithResponse(List transactionActions, Duration timeout, Context context) { + return blockWithOptionalTimeout(client.submitTransactionWithResponse(transactionActions, context), timeout); } } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java index c3a2da3e5b2f7..cda5f3e719659 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClientBuilder.java @@ -35,7 +35,7 @@ @ServiceClientBuilder(serviceClients = {TableClient.class, TableAsyncClient.class}) public final class TableClientBuilder { private static final SerializerAdapter TABLES_SERIALIZER = new TablesJacksonSerializer(); - private static final TablesMultipartSerializer BATCH_SERIALIZER = new TablesMultipartSerializer(); + private static final TablesMultipartSerializer TRANSACTIONAL_BATCH_SERIALIZER = new TablesMultipartSerializer(); private final ClientLogger logger = new ClientLogger(TableClientBuilder.class); private final List perCallPolicies = new ArrayList<>(); @@ -87,7 +87,8 @@ public TableAsyncClient buildAsyncClient() { azureNamedKeyCredential, azureSasCredential, sasToken, endpoint, retryPolicy, httpLogOptions, clientOptions, httpClient, perCallPolicies, perRetryPolicies, configuration, logger); - return new TableAsyncClient(tableName, pipeline, endpoint, serviceVersion, TABLES_SERIALIZER, BATCH_SERIALIZER); + return new TableAsyncClient(tableName, pipeline, endpoint, serviceVersion, TABLES_SERIALIZER, + TRANSACTIONAL_BATCH_SERIALIZER); } /** diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/BatchImpl.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/BatchImpl.java deleted file mode 100644 index 9bac602859a62..0000000000000 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/BatchImpl.java +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// Code generated by Microsoft (R) AutoRest Code Generator. - -package com.azure.data.tables.implementation; - -import com.azure.core.annotation.BodyParam; -import com.azure.core.annotation.ExpectedResponses; -import com.azure.core.annotation.HeaderParam; -import com.azure.core.annotation.Host; -import com.azure.core.annotation.HostParam; -import com.azure.core.annotation.Post; -import com.azure.core.annotation.ReturnType; -import com.azure.core.annotation.ServiceInterface; -import com.azure.core.annotation.ServiceMethod; -import com.azure.core.annotation.UnexpectedResponseExceptionType; -import com.azure.core.http.rest.RestProxy; -import com.azure.core.util.Context; -import com.azure.core.util.serializer.SerializerAdapter; -import com.azure.data.tables.implementation.models.BatchRequestBody; -import com.azure.data.tables.implementation.models.BatchSubmitBatchResponse; -import com.azure.data.tables.implementation.models.TableServiceErrorException; -import reactor.core.publisher.Mono; - -/** An instance of this class provides access to all the operations defined in Batch. */ -public final class BatchImpl { - /** The proxy service used to perform REST calls. */ - private final BatchService service; - - /** The service client containing this operation class. */ - private final AzureTableImpl client; - - /** - * Initializes an instance of BatchImpl. - * - * @param client the instance of the service client containing this operation class. - */ - public BatchImpl(AzureTableImpl client, SerializerAdapter serializerAdapter) { - this.service = RestProxy.create(BatchService.class, client.getHttpPipeline(), serializerAdapter); - this.client = client; - } - - /** - * The interface defining all the services for AzureTableBatch to be used by the proxy service to perform REST - * calls. - */ - @Host("{url}") - @ServiceInterface(name = "AzureTableServices") - private interface BatchService { - @Post("/$batch") - @ExpectedResponses({202}) - @UnexpectedResponseExceptionType(TableServiceErrorException.class) - Mono submitBatch( - @HostParam("url") String url, - @HeaderParam("Content-Type") String multipartContentType, - @HeaderParam("x-ms-version") String version, - @HeaderParam("x-ms-client-request-id") String requestId, - @HeaderParam("DataServiceVersion") String dataServiceVersion, - @BodyParam("multipart/mixed") BatchRequestBody body, - Context context); - } - - /** - * The Batch operation allows multiple API calls to be embedded into a single HTTP request. - * - * @param body Initial data. - * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the - * analytics logs when storage analytics logging is enabled. - * @param context The context to associate with this operation. - * @throws IllegalArgumentException thrown if parameters fail the validation. - * @return a Mono which performs the network request upon subscription. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public Mono submitBatchWithRestResponseAsync( - BatchRequestBody body, - String requestId, - Context context - ) { - final String dataServiceVersion = "3.0"; - return service.submitBatch(this.client.getUrl(), body.getContentType(), this.client.getVersion(), requestId, - dataServiceVersion, body, context); - } -} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/ModelHelper.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/ModelHelper.java index d8ec53de25937..277131b4a4ad3 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/ModelHelper.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/ModelHelper.java @@ -5,7 +5,7 @@ import com.azure.core.http.HttpRequest; import com.azure.core.util.logging.ClientLogger; import com.azure.data.tables.implementation.models.TableResponseProperties; -import com.azure.data.tables.models.BatchOperationResponse; +import com.azure.data.tables.models.TableTransactionActionResponse; import com.azure.data.tables.models.TableEntity; import com.azure.data.tables.models.TableItem; @@ -22,25 +22,26 @@ public final class ModelHelper { private static Supplier entityCreator; private static Function itemCreator; - private static BiFunction batchOperationResponseCreator; - private static BiConsumer batchOperationResponseUpdater; + private static BiFunction tableTransactionActionResponseCreator; + private static BiConsumer tableTransactionActionResponseUpdater; static { - // Force classes' static blocks to execute + // Force classes' static blocks to execute. try { Class.forName(TableEntity.class.getName(), true, TableEntity.class.getClassLoader()); Class.forName(TableItem.class.getName(), true, TableItem.class.getClassLoader()); - Class.forName(BatchOperationResponse.class.getName(), true, BatchOperationResponse.class.getClassLoader()); + Class.forName(TableTransactionActionResponse.class.getName(), true, + TableTransactionActionResponse.class.getClassLoader()); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(new ClientLogger(ModelHelper.class).logThrowableAsError(e)); } } /** - * Sets the entity creator. + * Sets the {@link TableEntity} creator. * - * @param creator The entity creator. - * @throws IllegalStateException if the creator has already been set. + * @param creator The {@link TableEntity} creator. + * @throws IllegalStateException If the creator has already been set. */ public static void setEntityCreator(Supplier creator) { Objects.requireNonNull(creator, "'creator' cannot be null."); @@ -54,10 +55,10 @@ public static void setEntityCreator(Supplier creator) { } /** - * Sets the item creator. + * Sets the {@link TableItem} creator. * - * @param creator The item creator. - * @throws IllegalStateException if the creator has already been set. + * @param creator The {@link TableItem} creator. + * @throws IllegalStateException If the creator has already been set. */ public static void setItemCreator(Function creator) { Objects.requireNonNull(creator, "'creator' cannot be null."); @@ -71,44 +72,44 @@ public static void setItemCreator(Function c } /** - * Sets the batch operation response creator. + * Sets the {@link TableTransactionActionResponse} creator. * * @param creator The creator. - * @throws IllegalStateException if the creator has already been set. + * @throws IllegalStateException If the creator has already been set. */ - public static void setBatchOperationResponseCreator(BiFunction creator) { + public static void setTableTransactionActionResponseCreator(BiFunction creator) { Objects.requireNonNull(creator, "'creator' cannot be null."); - if (ModelHelper.batchOperationResponseCreator != null) { + if (ModelHelper.tableTransactionActionResponseCreator != null) { throw new ClientLogger(ModelHelper.class).logExceptionAsError(new IllegalStateException( - "'batchOperationResponseCreator' is already set.")); + "'tableTransactionActionResponseCreator' is already set.")); } - batchOperationResponseCreator = creator; + tableTransactionActionResponseCreator = creator; } /** - * Sets the batch operation response updater. + * Sets the {@link TableTransactionActionResponse} updater. * * @param updater The updater. - * @throws IllegalStateException if the updater has already been set. + * @throws IllegalStateException If the updater has already been set. */ - public static void setBatchOperationResponseUpdater(BiConsumer updater) { + public static void setTableTransactionActionResponseUpdater(BiConsumer updater) { Objects.requireNonNull(updater, "'updater' cannot be null."); - if (ModelHelper.batchOperationResponseUpdater != null) { + if (ModelHelper.tableTransactionActionResponseUpdater != null) { throw new ClientLogger(ModelHelper.class).logExceptionAsError(new IllegalStateException( - "'batchOperationResponseUpdater' is already set.")); + "'tableTransactionActionResponseUpdater' is already set.")); } - batchOperationResponseUpdater = updater; + tableTransactionActionResponseUpdater = updater; } /** * Creates a {@link TableEntity}. * - * @param properties The properties used to construct the entity - * @return The created TableEntity + * @param properties The properties used to construct the {@link TableEntity}. + * @return The created {@link TableEntity}. */ public static TableEntity createEntity(Map properties) { if (entityCreator == null) { @@ -122,8 +123,8 @@ public static TableEntity createEntity(Map properties) { /** * Creates a {@link TableItem}. * - * @param properties The TableResponseProperties used to construct the table - * @return The created TableItem + * @param properties The {@link TableResponseProperties} used to construct the table. + * @return The created {@link TableItem}. */ public static TableItem createItem(TableResponseProperties properties) { if (itemCreator == null) { @@ -135,33 +136,33 @@ public static TableItem createItem(TableResponseProperties properties) { } /** - * Creates a {@link BatchOperationResponse}. + * Creates a {@link TableTransactionActionResponse}. * - * @param statusCode The status code for the BatchOperationResponse - * @param value The value for the BatchOperationResponse - * @return The created BatchOperationResponse + * @param statusCode The status code for the {@link TableTransactionActionResponse}. + * @param value The value for the {@link TableTransactionActionResponse}. + * @return The created {@link TableTransactionActionResponse}. */ - public static BatchOperationResponse createBatchOperationResponse(int statusCode, Object value) { - if (batchOperationResponseCreator == null) { + public static TableTransactionActionResponse createTableTransactionActionResponse(int statusCode, Object value) { + if (tableTransactionActionResponseCreator == null) { throw new ClientLogger(ModelHelper.class).logExceptionAsError( - new IllegalStateException("'batchOperationResponseCreator' should not be null.")); + new IllegalStateException("'tableTransactionActionResponseCreator' should not be null.")); } - return batchOperationResponseCreator.apply(statusCode, value); + return tableTransactionActionResponseCreator.apply(statusCode, value); } /** - * Updates a {@link BatchOperationResponse} with a request object. + * Updates a {@link TableTransactionActionResponse} with a request object. * - * @param subject The BatchOperationResponse to update - * @param request The request to attach to the BatchOperationResponse + * @param subject The {@link TableTransactionActionResponse} to update. + * @param request The request to attach to the {@link TableTransactionActionResponse}. */ - public static void updateBatchOperationResponse(BatchOperationResponse subject, HttpRequest request) { - if (batchOperationResponseUpdater == null) { + public static void updateTableTransactionActionResponse(TableTransactionActionResponse subject, HttpRequest request) { + if (tableTransactionActionResponseUpdater == null) { throw new ClientLogger(ModelHelper.class).logExceptionAsError( - new IllegalStateException("'batchOperationResponseUpdater' should not be null.")); + new IllegalStateException("'tableTransactionActionResponseUpdater' should not be null.")); } - batchOperationResponseUpdater.accept(subject, request); + tableTransactionActionResponseUpdater.accept(subject, request); } } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/SasProtocol.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/SasProtocol.java index e5b3165c00462..36463e7180b83 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/SasProtocol.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/SasProtocol.java @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. - package com.azure.data.tables.implementation; import java.util.Locale; diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/TablesMultipartSerializer.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/TablesMultipartSerializer.java index de556f144d331..cafd46b4a56ed 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/TablesMultipartSerializer.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/TablesMultipartSerializer.java @@ -9,11 +9,11 @@ import com.azure.core.util.FluxUtil; import com.azure.core.util.logging.ClientLogger; import com.azure.core.util.serializer.SerializerEncoding; -import com.azure.data.tables.implementation.models.BatchChangeSet; -import com.azure.data.tables.implementation.models.BatchSubRequest; +import com.azure.data.tables.implementation.models.TransactionalBatchChangeSet; +import com.azure.data.tables.implementation.models.TransactionalBatchSubRequest; import com.azure.data.tables.implementation.models.MultipartPart; import com.azure.data.tables.implementation.models.TableServiceError; -import com.azure.data.tables.models.BatchOperationResponse; +import com.azure.data.tables.models.TableTransactionActionResponse; import java.io.BufferedReader; import java.io.ByteArrayInputStream; @@ -34,14 +34,17 @@ public class TablesMultipartSerializer extends TablesJacksonSerializer { private static final String BOUNDARY_DELIMETER = "--"; private final ClientLogger logger = new ClientLogger(TablesMultipartSerializer.class); - private static class BatchOperationResponseParams { + private static class TableTransactionActionResponseBuilder { private int statusCode; private Object value; private final HttpHeaders headers = new HttpHeaders(); - BatchOperationResponse build() { - BatchOperationResponse response = ModelHelper.createBatchOperationResponse(statusCode, value); + TableTransactionActionResponse build() { + TableTransactionActionResponse response = + ModelHelper.createTableTransactionActionResponse(statusCode, value); + headers.forEach(h -> response.getHeaders().set(h.getName(), h.getValue())); + return response; } @@ -62,7 +65,7 @@ void putHeader(String name, String value) { public void serialize(Object object, SerializerEncoding encoding, OutputStream outputStream) throws IOException { if (object instanceof MultipartPart) { writeMultipartPart(object, encoding, outputStream); - } else if (object instanceof BatchSubRequest) { + } else if (object instanceof TransactionalBatchSubRequest) { writeRequest(object, outputStream); } else { super.serialize(object, encoding, outputStream); @@ -72,6 +75,7 @@ public void serialize(Object object, SerializerEncoding encoding, OutputStream o @Override public String serialize(Object object, SerializerEncoding encoding) throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); + serialize(object, encoding, stream); return stream.toString(StandardCharsets.UTF_8.name()); @@ -80,6 +84,7 @@ public String serialize(Object object, SerializerEncoding encoding) throws IOExc @Override public byte[] serializeToBytes(Object object, SerializerEncoding encoding) throws IOException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); + serialize(object, encoding, stream); return stream.toByteArray(); @@ -88,7 +93,7 @@ public byte[] serializeToBytes(Object object, SerializerEncoding encoding) throw private void writeMultipartPart(Object object, SerializerEncoding encoding, OutputStream os) throws IOException { MultipartPart part = (MultipartPart) object; - if (part instanceof BatchChangeSet) { + if (part instanceof TransactionalBatchChangeSet) { write("Content-Type: " + part.getContentType() + "\r\n\r\n", os); } @@ -102,7 +107,7 @@ private void writeMultipartPart(Object object, SerializerEncoding encoding, Outp } private void writeRequest(Object object, OutputStream os) throws IOException { - HttpRequest request = ((BatchSubRequest) object).getHttpRequest(); + HttpRequest request = ((TransactionalBatchSubRequest) object).getHttpRequest(); String method = request.getHttpMethod() == HttpMethod.PATCH ? "MERGE" : request.getHttpMethod().toString(); write("Content-Type: application/http\r\n", os); @@ -136,7 +141,7 @@ public U deserialize(String value, Type type, SerializerEncoding serializerE @Override public U deserialize(byte[] bytes, Type type, SerializerEncoding encoding) throws IOException { - if (type == BatchOperationResponse.class) { + if (type == TableTransactionActionResponse.class) { return deserialize(new ByteArrayInputStream(bytes), type, encoding); } else { return super.deserialize(bytes, type, encoding); @@ -145,19 +150,19 @@ public U deserialize(byte[] bytes, Type type, SerializerEncoding encoding) t @Override @SuppressWarnings("unchecked") - public U deserialize(InputStream inputStream, Type type, SerializerEncoding serializerEncoding) - throws IOException { - if (type == BatchOperationResponse[].class) { + public U deserialize(InputStream inputStream, Type type, SerializerEncoding serializerEncoding) throws IOException { + if (type == TableTransactionActionResponse[].class) { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); String line = reader.readLine(); + if (line == null || !line.startsWith(BOUNDARY_DELIMETER + "batchresponse_")) { - throw logger.logExceptionAsError(new IllegalStateException("Invalid multipart response")); + throw logger.logExceptionAsError(new IllegalStateException("Invalid multipart response.")); } - BatchOperationResponseParams responseParams = null; + TableTransactionActionResponseBuilder responseParams = null; boolean foundBody = false; String body = null; - List responses = new ArrayList<>(); + List responses = new ArrayList<>(); while ((line = reader.readLine()) != null) { if (line.startsWith(BOUNDARY_DELIMETER + "changesetresponse_")) { @@ -167,7 +172,7 @@ public U deserialize(InputStream inputStream, Type type, SerializerEncoding responseParams.setValue(deserialize(body, TableServiceError.class, serializerEncoding)); } catch (IOException e) { logger.logThrowableAsWarning( - new IOException("Unable to deserialize sub-response body", e)); + new IOException("Unable to deserialize sub-response body.", e)); responseParams.setValue(body); } } @@ -177,12 +182,12 @@ public U deserialize(InputStream inputStream, Type type, SerializerEncoding foundBody = false; body = null; } else if (responseParams == null && line.startsWith("HTTP/1.1")) { - responseParams = new BatchOperationResponseParams(); - // Characters 9-12 in the first line of the HTTP response are the status code + responseParams = new TableTransactionActionResponseBuilder(); + // Characters 9-12 in the first line of the HTTP response are the status code. responseParams.setStatusCode(Integer.parseInt(line.substring(9, 12))); } else if (responseParams != null && !foundBody) { if (line.isEmpty()) { - // An empty line after the headers delimits the body + // An empty line after the headers delimits the body. foundBody = true; } else { // A header line @@ -190,12 +195,12 @@ public U deserialize(InputStream inputStream, Type type, SerializerEncoding responseParams.putHeader(header[0].trim(), header[1].trim()); } } else if (responseParams != null && !line.isEmpty()) { - // The rest of the lines constitute the body until we get to the delimiter again + // The rest of the lines constitute the body until we get to the delimiter again. body = (body == null ? line : body + line) + "\r\n"; } } - return (U) responses.toArray(new BatchOperationResponse[0]); + return (U) responses.toArray(new TableTransactionActionResponse[0]); } else { return super.deserialize(inputStream, type, serializerEncoding); } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/TransactionalBatchImpl.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/TransactionalBatchImpl.java new file mode 100644 index 0000000000000..371ff03403f5f --- /dev/null +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/TransactionalBatchImpl.java @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables.implementation; + +import com.azure.core.annotation.BodyParam; +import com.azure.core.annotation.ExpectedResponses; +import com.azure.core.annotation.HeaderParam; +import com.azure.core.annotation.Host; +import com.azure.core.annotation.HostParam; +import com.azure.core.annotation.Post; +import com.azure.core.annotation.ReturnType; +import com.azure.core.annotation.ServiceInterface; +import com.azure.core.annotation.ServiceMethod; +import com.azure.core.annotation.UnexpectedResponseExceptionType; +import com.azure.core.http.rest.RestProxy; +import com.azure.core.util.Context; +import com.azure.core.util.serializer.SerializerAdapter; +import com.azure.data.tables.implementation.models.TransactionalBatchRequestBody; +import com.azure.data.tables.implementation.models.TransactionalBatchResponse; +import com.azure.data.tables.implementation.models.TableServiceErrorException; +import reactor.core.publisher.Mono; + +/** + * An instance of this class provides access to transactional batch operations. + */ +public final class TransactionalBatchImpl { + /** + * The proxy service used to perform REST calls. + */ + private final TransactionalBatchService service; + + /** + * The service client containing this operation class. + */ + private final AzureTableImpl client; + + /** + * Initializes an instance of {@link TransactionalBatchImpl}. + * + * @param client The instance of the service client containing this operation class. + * @param transactionalBatchSerializer Serializer adapter used to handle requests and responses in this + * implementation client. + */ + public TransactionalBatchImpl(AzureTableImpl client, SerializerAdapter transactionalBatchSerializer) { + this.service = RestProxy.create(TransactionalBatchService.class, client.getHttpPipeline(), transactionalBatchSerializer); + this.client = client; + } + + /** + * The interface defining all the services for {@link TransactionalBatchImpl} to be used by the proxy service to + * perform REST calls. + */ + @Host("{url}") + @ServiceInterface(name = "AzureTableServices") + private interface TransactionalBatchService { + @Post("/$batch") + @ExpectedResponses({202}) + @UnexpectedResponseExceptionType(TableServiceErrorException.class) + Mono submitTransactionalBatch( + @HostParam("url") String url, + @HeaderParam("Content-Type") String multipartContentType, + @HeaderParam("x-ms-version") String version, + @HeaderParam("x-ms-client-request-id") String requestId, + @HeaderParam("DataServiceVersion") String dataServiceVersion, + @BodyParam("multipart/mixed") TransactionalBatchRequestBody body, + Context context); + } + + /** + * The submit transactional batch operation allows multiple API calls to be embedded into a single HTTP request. + * + * @param body Initial data. + * @param requestId Provides a client-generated, opaque value with a 1 KB character limit that is recorded in the + * analytics logs when storage analytics logging is enabled. + * @param context The context to associate with this operation. + * + * @return A reactive result containing a {@link TransactionalBatchResponse}. + * + * @throws IllegalArgumentException If parameters fail validation. + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono submitTransactionalBatchWithRestResponseAsync(TransactionalBatchRequestBody body, String requestId, Context context) { + final String dataServiceVersion = "3.0"; + + return service.submitTransactionalBatch(this.client.getUrl(), body.getContentType(), this.client.getVersion(), requestId, + dataServiceVersion, body, context); + } +} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubmitBatchHeaders.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubmitBatchHeaders.java deleted file mode 100644 index b4e8f8952f4b9..0000000000000 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubmitBatchHeaders.java +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// Code generated by Microsoft (R) AutoRest Code Generator. - -package com.azure.data.tables.implementation.models; - -import com.azure.core.annotation.Fluent; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * Defines headers for SubmitBatch operation. - */ -@Fluent -public final class BatchSubmitBatchHeaders { - /* - * The media type of the body of the response. For batch requests, this is - * multipart/mixed; boundary=batchresponse_GUID - */ - @JsonProperty(value = "Content-Type") - private String contentType; - - /* - * The x-ms-version property. - */ - @JsonProperty(value = "x-ms-version") - private String xMsVersion; - - /* - * The x-ms-request-id property. - */ - @JsonProperty(value = "x-ms-request-id") - private String xMsRequestId; - - /* - * The x-ms-client-request-id property. - */ - @JsonProperty(value = "x-ms-client-request-id") - private String xMsClientRequestId; - - /** - * Get the contentType property: The media type of the body of the - * response. For batch requests, this is - * multipart/mixed; boundary=batchresponse_GUID. - * - * @return the contentType value. - */ - public String getContentType() { - return this.contentType; - } - - /** - * Set the contentType property: The media type of the body of the - * response. For batch requests, this is multipart/mixed; - * boundary=batch_GUID. - * - * @param contentType the contentType value to set. - * @return the BatchSubmitBatchHeaders object itself. - */ - public BatchSubmitBatchHeaders setContentType(String contentType) { - this.contentType = contentType; - return this; - } - - /** - * Get the xMsVersion property: The x-ms-version property. - * - * @return the xMsVersion value. - */ - public String getXMsVersion() { - return this.xMsVersion; - } - - /** - * Set the xMsVersion property: The x-ms-version property. - * - * @param xMsVersion the xMsVersion value to set. - * @return the BatchSubmitBatchHeaders object itself. - */ - public BatchSubmitBatchHeaders setXMsVersion(String xMsVersion) { - this.xMsVersion = xMsVersion; - return this; - } - - /** - * Get the xMsRequestId property: The x-ms-request-id property. - * - * @return the xMsRequestId value. - */ - public String getXMsRequestId() { - return this.xMsRequestId; - } - - /** - * Set the xMsRequestId property: The x-ms-request-id property. - * - * @param xMsRequestId the xMsRequestId value to set. - * @return the BatchSubmitBatchHeaders object itself. - */ - public BatchSubmitBatchHeaders setXMsRequestId(String xMsRequestId) { - this.xMsRequestId = xMsRequestId; - return this; - } - - /** - * Get the xMsClientRequestId property: The x-ms-client-request-id property. - * - * @return the xMsClientRequestId value. - */ - public String getXMsClientRequestId() { - return this.xMsClientRequestId; - } - - /** - * Set the xMsClientRequestId property: The x-ms-client-request-id property. - * - * @param xMsClientRequestId the xMsClientRequestId value to set. - * @return the BatchSubmitBatchHeaders object itself. - */ - public BatchSubmitBatchHeaders setXMsClientRequestId(String xMsClientRequestId) { - this.xMsClientRequestId = xMsClientRequestId; - return this; - } -} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubmitBatchResponse.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubmitBatchResponse.java deleted file mode 100644 index 2fad8f47fe48b..0000000000000 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubmitBatchResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// Code generated by Microsoft (R) AutoRest Code Generator. - -package com.azure.data.tables.implementation.models; - -import com.azure.core.http.HttpHeaders; -import com.azure.core.http.HttpRequest; -import com.azure.core.http.rest.ResponseBase; -import com.azure.data.tables.models.BatchOperationResponse; - -/** - * Contains all response data for the submitBatch operation. - */ -public final class BatchSubmitBatchResponse extends ResponseBase { - /** - * Creates an instance of BatchSubmitBatchResponse. - * - * @param request the request which resulted in this BatchSubmitBatchResponse. - * @param statusCode the status code of the HTTP response. - * @param rawHeaders the raw headers of the HTTP response. - * @param value the content stream. - * @param headers the deserialized headers of the HTTP response. - */ - public BatchSubmitBatchResponse(HttpRequest request, int statusCode, HttpHeaders rawHeaders, - BatchOperationResponse[] value, BatchSubmitBatchHeaders headers) { - super(request, statusCode, rawHeaders, value, headers); - } - - /** - * @return the response content stream. - */ - @Override - public BatchOperationResponse[] getValue() { - return super.getValue(); - } -} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/MultipartPart.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/MultipartPart.java index 21104970d3663..df99720ee2608 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/MultipartPart.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/MultipartPart.java @@ -16,7 +16,7 @@ public abstract class MultipartPart { private final List contents = new ArrayList<>(); public MultipartPart(String boundaryPrefix) { - this.boundary = boundaryPrefix + "_" + UUID.randomUUID().toString(); + this.boundary = boundaryPrefix + "_" + UUID.randomUUID(); } public String getContentType() { @@ -33,6 +33,7 @@ public List getContents() { protected MultipartPart addContent(T content) { contents.add(content); + return this; } } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TableServiceErrorOdataError.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TableServiceErrorOdataError.java index 2d5b65956d7d8..ddb09107f250e 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TableServiceErrorOdataError.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TableServiceErrorOdataError.java @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// Code generated by Microsoft (R) AutoRest Code Generator. package com.azure.data.tables.implementation.models; diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TableServiceErrorOdataErrorMessage.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TableServiceErrorOdataErrorMessage.java index 3814c63c73597..0070d357f8b98 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TableServiceErrorOdataErrorMessage.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TableServiceErrorOdataErrorMessage.java @@ -1,6 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -// Code generated by Microsoft (R) AutoRest Code Generator. package com.azure.data.tables.implementation.models; diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchOperation.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchAction.java similarity index 93% rename from sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchOperation.java rename to sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchAction.java index 8382737f32ff0..a9e3e5fbd2250 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchOperation.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchAction.java @@ -9,11 +9,10 @@ import com.azure.data.tables.models.TableEntityUpdateMode; import reactor.core.publisher.Mono; -public interface BatchOperation { - +public interface TransactionalBatchAction { Mono prepareRequest(TableAsyncClient preparer); - class CreateEntity implements BatchOperation { + class CreateEntity implements TransactionalBatchAction { private final TableEntity entity; public CreateEntity(TableEntity entity) { @@ -38,7 +37,7 @@ public String toString() { } } - class UpsertEntity implements BatchOperation { + class UpsertEntity implements TransactionalBatchAction { private final TableEntity entity; private final TableEntityUpdateMode updateMode; @@ -70,7 +69,7 @@ public String toString() { } } - class UpdateEntity implements BatchOperation { + class UpdateEntity implements TransactionalBatchAction { private final TableEntity entity; private final TableEntityUpdateMode updateMode; private final boolean ifUnchanged; @@ -109,7 +108,7 @@ public String toString() { } } - class DeleteEntity implements BatchOperation { + class DeleteEntity implements TransactionalBatchAction { private final String partitionKey; private final String rowKey; private final String eTag; diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchChangeSet.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchChangeSet.java similarity index 62% rename from sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchChangeSet.java rename to sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchChangeSet.java index 2309e980ba212..449b71333eb3d 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchChangeSet.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchChangeSet.java @@ -5,8 +5,8 @@ import com.azure.core.annotation.Fluent; @Fluent -public final class BatchChangeSet extends MultipartPart { - public BatchChangeSet() { +public final class TransactionalBatchChangeSet extends MultipartPart { + public TransactionalBatchChangeSet() { super("changeset"); } } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchRequestBody.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchRequestBody.java similarity index 50% rename from sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchRequestBody.java rename to sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchRequestBody.java index ecfe618817615..f1d75684e94f8 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchRequestBody.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchRequestBody.java @@ -6,35 +6,42 @@ import com.azure.core.util.logging.ClientLogger; @Fluent -public final class BatchRequestBody extends MultipartPart { - private final ClientLogger logger = new ClientLogger(BatchRequestBody.class); - private BatchChangeSet changeSet = null; +public final class TransactionalBatchRequestBody extends MultipartPart { + private final ClientLogger logger = new ClientLogger(TransactionalBatchRequestBody.class); + private TransactionalBatchChangeSet changeSet = null; private boolean queryAdded = false; - public BatchRequestBody() { + public TransactionalBatchRequestBody() { super("batch"); } - public BatchRequestBody addQueryOperation(BatchSubRequest queryRequest) { + public TransactionalBatchRequestBody addQueryOperation(TransactionalBatchSubRequest queryRequest) { if (changeSet != null) { throw logger.logExceptionAsError(new IllegalStateException( - "Cannot add a query operation to a BatchRequestBody containing a changeset.")); + "Cannot add a query operation to a TransactionalBatchRequestBody containing a changeset.")); } + addContent(queryRequest); + queryAdded = true; + return this; } - public BatchRequestBody addChangeOperation(BatchSubRequest changeRequest) { + public TransactionalBatchRequestBody addChangeOperation(TransactionalBatchSubRequest changeRequest) { if (queryAdded) { throw logger.logExceptionAsError(new IllegalStateException( - "Cannot add a change operation to a BatchRequestBody containing query operations.")); + "Cannot add a change operation to a TransactionalBatchRequestBody containing query operations.")); } + if (changeSet == null) { - changeSet = new BatchChangeSet(); + changeSet = new TransactionalBatchChangeSet(); + addContent(changeSet); } + changeSet.addContent(changeRequest); + return this; } } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchResponse.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchResponse.java new file mode 100644 index 0000000000000..0d5da05568fbd --- /dev/null +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchResponse.java @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables.implementation.models; + +import com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpRequest; +import com.azure.core.http.rest.ResponseBase; +import com.azure.core.util.Context; +import com.azure.data.tables.implementation.TransactionalBatchImpl; +import com.azure.data.tables.models.TableTransactionActionResponse; + +/** + * Contains all response data for the + * {@link TransactionalBatchImpl#submitTransactionalBatchWithRestResponseAsync(TransactionalBatchRequestBody, String, Context)} + * operation. + */ +public final class TransactionalBatchResponse extends ResponseBase { + /** + * Creates an instance of {@link TransactionalBatchResponse}. + * + * @param request The request which resulted in this {@link TransactionalBatchResponse}. + * @param statusCode The status code of the HTTP response. + * @param rawHeaders The raw headers of the HTTP response. + * @param value The content stream. + * @param headers The deserialized headers of the HTTP response. + */ + public TransactionalBatchResponse(HttpRequest request, int statusCode, HttpHeaders rawHeaders, + TableTransactionActionResponse[] value, TransactionalBatchSubmitBatchHeaders headers) { + super(request, statusCode, rawHeaders, value, headers); + } + + /** + * @return The response content stream. + */ + @Override + public TableTransactionActionResponse[] getValue() { + return super.getValue(); + } +} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubRequest.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchSubRequest.java similarity index 62% rename from sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubRequest.java rename to sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchSubRequest.java index 9053e7e1cf219..a0442007000f5 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/BatchSubRequest.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchSubRequest.java @@ -4,16 +4,16 @@ import com.azure.core.http.HttpRequest; -public final class BatchSubRequest { - private final BatchOperation operation; +public final class TransactionalBatchSubRequest { + private final TransactionalBatchAction operation; private final HttpRequest httpRequest; - public BatchSubRequest(BatchOperation operation, HttpRequest httpRequest) { + public TransactionalBatchSubRequest(TransactionalBatchAction operation, HttpRequest httpRequest) { this.operation = operation; this.httpRequest = httpRequest; } - public BatchOperation getOperation() { + public TransactionalBatchAction getOperation() { return operation; } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchSubmitBatchHeaders.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchSubmitBatchHeaders.java new file mode 100644 index 0000000000000..1c97753a68631 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchSubmitBatchHeaders.java @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables.implementation.models; + +import com.azure.core.annotation.Fluent; +import com.azure.core.util.Context; +import com.azure.data.tables.implementation.TransactionalBatchImpl; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Defines headers for the + * {@link TransactionalBatchImpl#submitTransactionalBatchWithRestResponseAsync(TransactionalBatchRequestBody, String, Context)} + * operation. + */ +@Fluent +public final class TransactionalBatchSubmitBatchHeaders { + /* + * The media type of the body of the response. For transactional batch requests, this is + * "multipart/mixed; boundary=batchresponse_GUID". + */ + @JsonProperty(value = "Content-Type") + private String contentType; + + /* + * The x-ms-version property. + */ + @JsonProperty(value = "x-ms-version") + private String xMsVersion; + + /* + * The x-ms-request-id property. + */ + @JsonProperty(value = "x-ms-request-id") + private String xMsRequestId; + + /* + * The x-ms-client-request-id property. + */ + @JsonProperty(value = "x-ms-client-request-id") + private String xMsClientRequestId; + + /** + * Get the media type of the body of the response. For transactional batch requests, this is + * "multipart/mixed; boundary=batchresponse_GUID". + * + * @return The content type. + */ + public String getContentType() { + return this.contentType; + } + + /** + * Set the contentType property: The media type of the body of the response. For transactional batch requests, + * this is "multipart/mixed; boundary=batch_GUID". + * + * @param contentType the contentType value to set. + * @return The updated {@link TransactionalBatchSubmitBatchHeaders} object. + */ + public TransactionalBatchSubmitBatchHeaders setContentType(String contentType) { + this.contentType = contentType; + return this; + } + + /** + * Get the {@code x-ms-version} property. + * + * @return The {@code x-ms-version}. + */ + public String getXMsVersion() { + return this.xMsVersion; + } + + /** + * Set the {@code x-ms-version} property. + * + * @param xMsVersion The {@code x-ms-version} to set. + * @return The updated {@link TransactionalBatchSubmitBatchHeaders} object. + */ + public TransactionalBatchSubmitBatchHeaders setXMsVersion(String xMsVersion) { + this.xMsVersion = xMsVersion; + return this; + } + + /** + * Get the {@code x-ms-request-id} property. + * + * @return The {@code x-ms-request-id}. + */ + public String getXMsRequestId() { + return this.xMsRequestId; + } + + /** + * Set the {@code x-ms-request-id} property. + * + * @param xMsRequestId The {@code x-ms-request-id} to set. + * @return The updated {@link TransactionalBatchSubmitBatchHeaders} object. + */ + public TransactionalBatchSubmitBatchHeaders setXMsRequestId(String xMsRequestId) { + this.xMsRequestId = xMsRequestId; + return this; + } + + /** + * Get the {@code x-ms-client-request-id} property. + * + * @return The {@code x-ms-client-request-id}. + */ + public String getXMsClientRequestId() { + return this.xMsClientRequestId; + } + + /** + * Set the {@code x-ms-client-request-id} property. + * + * @param xMsClientRequestId The {@code x-ms-client-request-id} to set. + * @return The updated {@link TransactionalBatchSubmitBatchHeaders} object. + */ + public TransactionalBatchSubmitBatchHeaders setXMsClientRequestId(String xMsClientRequestId) { + this.xMsClientRequestId = xMsClientRequestId; + return this; + } +} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableServiceException.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableServiceException.java index 474c538a74868..f8e7e6e3ae66d 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableServiceException.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableServiceException.java @@ -8,7 +8,7 @@ /** * Exception thrown for an invalid response with {@link TableServiceError} information. */ -public final class TableServiceException extends HttpResponseException { +public class TableServiceException extends HttpResponseException { /** * Initializes a new instance of the {@link TableServiceException} class. * diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java index d01dc67272949..cd20ff5ab36c6 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java @@ -3,7 +3,7 @@ package com.azure.data.tables.models; /** - * Defines an transaction action to be included as part of a batch operation. + * Defines an action to be included as part of a transactional batch operation. */ public class TableTransactionAction { private final TableTransactionActionType actionType; diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/BatchOperationResponse.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionResponse.java similarity index 66% rename from sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/BatchOperationResponse.java rename to sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionResponse.java index 2a9412596d58b..38a654d05c8c1 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/BatchOperationResponse.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionResponse.java @@ -8,32 +8,32 @@ import com.azure.data.tables.implementation.ModelHelper; /** - * The response of a REST sub-request contained within the response to a Batch request. + * The response of a REST sub-request contained within the response of a transaction request. */ -public final class BatchOperationResponse implements Response { +public final class TableTransactionActionResponse implements Response { private HttpRequest request; private final int statusCode; private final HttpHeaders headers; private final Object value; static { - ModelHelper.setBatchOperationResponseCreator(BatchOperationResponse::new); - ModelHelper.setBatchOperationResponseUpdater(BatchOperationResponse::update); + ModelHelper.setTableTransactionActionResponseCreator(TableTransactionActionResponse::new); + ModelHelper.setTableTransactionActionResponseUpdater(TableTransactionActionResponse::update); } /** - * Creates a new empty {@link BatchOperationResponse} + * Creates a new empty {@link TableTransactionActionResponse} */ - private BatchOperationResponse(int statusCode, Object value) { + private TableTransactionActionResponse(int statusCode, Object value) { this.headers = new HttpHeaders(); this.statusCode = statusCode; this.value = value; } /** - * Gets the sub-request which resulted in this {@link BatchOperationResponse}. + * Gets the sub-request which resulted in this {@link TableTransactionActionResponse}. * - * @return The sub-request which resulted in this {@link BatchOperationResponse}. + * @return The sub-request which resulted in this {@link TableTransactionActionResponse}. */ @Override public HttpRequest getRequest() { @@ -70,7 +70,7 @@ public Object getValue() { return value; } - private static void update(BatchOperationResponse subject, HttpRequest request) { + private static void update(TableTransactionActionResponse subject, HttpRequest request) { subject.request = request; } } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java index 17c00bae040e6..340fc5ae9698c 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java @@ -5,7 +5,7 @@ import com.azure.data.tables.TableClient; /** - * The type of operation to be executed on a table entity as part of a table transactional batch of operations. + * The type of action to be executed on a {@link TableEntity} in a transactional batch operation. */ public enum TableTransactionActionType { /** diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionFailedException.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionFailedException.java new file mode 100644 index 0000000000000..06d45cbd1d94e --- /dev/null +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionFailedException.java @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables.models; + +import com.azure.core.http.HttpResponse; +import com.azure.core.util.Context; +import com.azure.data.tables.TableAsyncClient; +import com.azure.data.tables.TableClient; + +import java.time.Duration; +import java.util.List; + +/** + * Exception thrown for an invalid response on a transactional operation with {@link TableServiceError} information. + */ +public class TableTransactionFailedException extends TableServiceException { + private final Integer failedTransactionActionIndex; + + /** + * Initializes a new instance of the {@link TableTransactionFailedException} class. + * + * @param message The exception message or the response content if a message is not available. + * @param response The HTTP response. + * @param value The deserialized response value. + * @param failedTransactionActionIndex The index position of the failed {@link TableTransactionAction} in the + * collection submitted to {@link TableClient#submitTransaction(List)}, + * {@link TableClient#submitTransactionWithResponse(List, Duration, Context)}, + * {@link TableAsyncClient#submitTransaction(List)} or {@link TableAsyncClient#submitTransactionWithResponse(List)} + * which caused the transaction to fail. If {@code null}, it means the service did not indicate which + * {@link TableTransactionAction} failed. + */ + public TableTransactionFailedException(String message, HttpResponse response, TableServiceError value, + Integer failedTransactionActionIndex) { + super(message, response, value); + + this.failedTransactionActionIndex = failedTransactionActionIndex; + } + + /** + * Get the index position of the failed {@link TableTransactionAction} in the collection submitted to + * {@link TableClient#submitTransaction(List)}, + * {@link TableClient#submitTransactionWithResponse(List, Duration, Context)}, + * {@link TableAsyncClient#submitTransaction(List)} or {@link TableAsyncClient#submitTransactionWithResponse(List)} + * which caused the transaction to fail. If {@code null}, it means the service did not indicate which + * {@link TableTransactionAction} failed. + * + * @return The index of the failed {@link TableTransactionAction}. + */ + public Integer getFailedTransactionActionIndex() { + return failedTransactionActionIndex; + } +} diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionResult.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionResult.java new file mode 100644 index 0000000000000..3c0bfbc7ffa71 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionResult.java @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables.models; + +import com.azure.core.util.Context; +import com.azure.data.tables.TableAsyncClient; +import com.azure.data.tables.TableClient; + +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A result type returned from calling {@link TableClient#submitTransaction(List)}, + * {@link TableClient#submitTransactionWithResponse(List, Duration, Context)}, + * {@link TableAsyncClient#submitTransaction(List)} or {@link TableAsyncClient#submitTransactionWithResponse(List)}. + */ +public class TableTransactionResult { + private final List transactionActionResponses; + private final Map lookupMap; + + /** + * Create a new {@link TableTransactionResult}. + * + * @param transactionActions The list of {@link TableTransactionAction transaction actions} sent in the request. + * @param transactionActionResponses The list of {@link TableTransactionActionResponse responses} that correspond + * to each transaction action. + */ + public TableTransactionResult(List transactionActions, + List transactionActionResponses) { + this.transactionActionResponses = transactionActionResponses; + this.lookupMap = new HashMap<>(); + + if (transactionActions.size() == transactionActionResponses.size()) { + for (int i = 0; i < transactionActions.size(); i++) { + lookupMap.put(transactionActions.get(i).getEntity().getRowKey(), transactionActionResponses.get(i)); + } + + System.out.println("Done creating lookup map"); + } else { + System.out.println("Different sizes WTF."); + } + } + + /** + * Get all the {@link TableTransactionActionResponse sub-responses} obtained from the submit transaction operation. + * + * @return The {@link TableTransactionActionResponse sub-responses} obtained from the submit transaction operation + */ + public List getTransactionActionResponses() { + return transactionActionResponses; + } + + /** + * Obtain the corresponding {@link TableTransactionActionResponse sub-response} for a given {@code rowKey}. + * + * @param rowKey The {@code rowKey} to look a {@link TableTransactionActionResponse sub-response} with. + * + * @return The {@link TableTransactionActionResponse} that corresponds to the given {@code rowKey}. + */ + public TableTransactionActionResponse getTableTransactionActionResponseByRowKey(String rowKey) { + return lookupMap.get(rowKey); + } +} diff --git a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java index d1fc454593775..de9c10a626bd2 100644 --- a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java +++ b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java @@ -12,13 +12,15 @@ import com.azure.core.http.rest.Response; import com.azure.core.test.TestBase; import com.azure.core.test.utils.TestResourceNamer; -import com.azure.data.tables.models.BatchOperationResponse; +import com.azure.data.tables.models.TableTransactionActionResponse; import com.azure.data.tables.models.ListEntitiesOptions; import com.azure.data.tables.models.TableEntity; import com.azure.data.tables.models.TableEntityUpdateMode; import com.azure.data.tables.models.TableServiceException; import com.azure.data.tables.models.TableTransactionAction; import com.azure.data.tables.models.TableTransactionActionType; +import com.azure.data.tables.models.TableTransactionFailedException; +import com.azure.data.tables.models.TableTransactionResult; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -730,14 +732,16 @@ void submitTransactionAsync() { TableTransactionActionType.CREATE, new TableEntity(partitionKeyValue, rowKeyValue2))); // Act & Assert - final Response> result = + final Response result = tableClient.submitTransactionWithResponse(transactionalBatch).block(TIMEOUT); assertNotNull(result); assertEquals(expectedBatchStatusCode, result.getStatusCode()); - assertEquals(transactionalBatch.size(), result.getValue().size()); - assertEquals(expectedOperationStatusCode, result.getValue().get(0).getStatusCode()); - assertEquals(expectedOperationStatusCode, result.getValue().get(1).getStatusCode()); + assertEquals(transactionalBatch.size(), result.getValue().getTransactionActionResponses().size()); + assertEquals(expectedOperationStatusCode, + result.getValue().getTransactionActionResponses().get(0).getStatusCode()); + assertEquals(expectedOperationStatusCode, + result.getValue().getTransactionActionResponses().get(1).getStatusCode()); StepVerifier.create(tableClient.getEntityWithResponse(partitionKeyValue, rowKeyValue, null)) .assertNext(response -> { @@ -804,9 +808,9 @@ void submitTransactionAsyncAllOperations() { .assertNext(response -> { assertNotNull(response); assertEquals(expectedBatchStatusCode, response.getStatusCode()); - List subResponses = response.getValue(); - assertEquals(transactionalBatch.size(), subResponses.size()); - for (BatchOperationResponse subResponse : subResponses) { + TableTransactionResult result = response.getValue(); + assertEquals(transactionalBatch.size(), result.getTransactionActionResponses().size()); + for (TableTransactionActionResponse subResponse : result.getTransactionActionResponses()) { assertEquals(expectedOperationStatusCode, subResponse.getStatusCode()); } }) @@ -829,7 +833,7 @@ void submitTransactionAsyncWithFailingOperation() { // Act & Assert StepVerifier.create(tableClient.submitTransactionWithResponse(transactionalBatch)) - .expectErrorMatches(e -> e instanceof TableServiceException + .expectErrorMatches(e -> e instanceof TableTransactionFailedException && e.getMessage().contains("An operation within the batch failed") && e.getMessage().contains("The failed operation was") && e.getMessage().contains("DeleteEntity") From f45631115f77b01ca88b7d6fa58e5b09cd509527 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Tue, 25 May 2021 18:47:11 -0500 Subject: [PATCH 06/16] Added more tests. --- .../data/tables/TablesAsyncClientTest.java | 52 +++++++++++++++++- ...est.submitTransactionAsyncAllActions.json} | 0 ...actionAsyncWithDifferentPartitionKeys.json | 53 +++++++++++++++++++ ...mitTransactionAsyncWithFailingAction.json} | 0 ...submitTransactionAsyncWithSameRowKeys.json | 53 +++++++++++++++++++ 5 files changed, 156 insertions(+), 2 deletions(-) rename sdk/tables/azure-data-tables/src/test/resources/session-records/{TablesAsyncClientTest.submitTransactionAsyncAllOperations.json => TablesAsyncClientTest.submitTransactionAsyncAllActions.json} (100%) create mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithDifferentPartitionKeys.json rename sdk/tables/azure-data-tables/src/test/resources/session-records/{TablesAsyncClientTest.submitTransactionAsyncWithFailingOperation.json => TablesAsyncClientTest.submitTransactionAsyncWithFailingAction.json} (100%) create mode 100644 sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithSameRowKeys.json diff --git a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java index de9c10a626bd2..0ee6a09972f33 100644 --- a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java +++ b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java @@ -760,7 +760,7 @@ void submitTransactionAsync() { @Test @Tag("Batch") - void submitTransactionAsyncAllOperations() { + void submitTransactionAsyncAllActions() { String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); String rowKeyValueCreate = testResourceNamer.randomName("rowKey", 20); String rowKeyValueUpsertInsert = testResourceNamer.randomName("rowKey", 20); @@ -820,7 +820,7 @@ void submitTransactionAsyncAllOperations() { @Test @Tag("Batch") - void submitTransactionAsyncWithFailingOperation() { + void submitTransactionAsyncWithFailingAction() { String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); String rowKeyValue = testResourceNamer.randomName("rowKey", 20); String rowKeyValue2 = testResourceNamer.randomName("rowKey", 20); @@ -841,4 +841,52 @@ void submitTransactionAsyncWithFailingOperation() { && e.getMessage().contains("rowKey='" + rowKeyValue2)) .verify(); } + + @Test + @Tag("Batch") + void submitTransactionAsyncWithSameRowKeys() { + String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); + String rowKeyValue = testResourceNamer.randomName("rowKey", 20); + + List transactionalBatch = new ArrayList<>(); + transactionalBatch.add(new TableTransactionAction( + TableTransactionActionType.CREATE, new TableEntity(partitionKeyValue, rowKeyValue))); + transactionalBatch.add(new TableTransactionAction( + TableTransactionActionType.CREATE, new TableEntity(partitionKeyValue, rowKeyValue))); + + // Act & Assert + StepVerifier.create(tableClient.submitTransactionWithResponse(transactionalBatch)) + .expectErrorMatches(e -> e instanceof TableTransactionFailedException + && e.getMessage().contains("An operation within the batch failed") + && e.getMessage().contains("The failed operation was") + && e.getMessage().contains("CreateEntity") + && e.getMessage().contains("partitionKey='" + partitionKeyValue) + && e.getMessage().contains("rowKey='" + rowKeyValue)) + .verify(); + } + + @Test + @Tag("Batch") + void submitTransactionAsyncWithDifferentPartitionKeys() { + String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); + String partitionKeyValue2 = testResourceNamer.randomName("partitionKey", 20); + String rowKeyValue = testResourceNamer.randomName("rowKey", 20); + String rowKeyValue2 = testResourceNamer.randomName("rowKey", 20); + + List transactionalBatch = new ArrayList<>(); + transactionalBatch.add(new TableTransactionAction( + TableTransactionActionType.CREATE, new TableEntity(partitionKeyValue, rowKeyValue))); + transactionalBatch.add(new TableTransactionAction( + TableTransactionActionType.CREATE, new TableEntity(partitionKeyValue2, rowKeyValue2))); + + // Act & Assert + StepVerifier.create(tableClient.submitTransactionWithResponse(transactionalBatch)) + .expectErrorMatches(e -> e instanceof TableTransactionFailedException + && e.getMessage().contains("An operation within the batch failed") + && e.getMessage().contains("The failed operation was") + && e.getMessage().contains("CreateEntity") + && e.getMessage().contains("partitionKey='" + partitionKeyValue2) + && e.getMessage().contains("rowKey='" + rowKeyValue2)) + .verify(); + } } diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncAllOperations.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncAllActions.json similarity index 100% rename from sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncAllOperations.json rename to sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncAllActions.json diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithDifferentPartitionKeys.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithDifferentPartitionKeys.json new file mode 100644 index 0000000000000..dc0e5a5a4e9c3 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithDifferentPartitionKeys.json @@ -0,0 +1,53 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/Tables", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "e7766208-335b-4d8a-bee5-9a05af36199b", + "Content-Type" : "application/json;odata=nometadata" + }, + "Response" : { + "content-length" : "0", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "204", + "Date" : "Tue, 25 May 2021 23:42:24 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename20677345')", + "x-ms-request-id" : "28a6ff22-5002-0009-2abf-51c4ef000000", + "x-ms-client-request-id" : "e7766208-335b-4d8a-bee5-9a05af36199b", + "Preference-Applied" : "return-no-content", + "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename20677345')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/$batch", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "75023aaf-6872-466d-8533-6e6f133f27f0", + "Content-Type" : "multipart/mixed; boundary=batch_38a5907e-d51a-4609-8a3f-e634fae8523a" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2019-02-02", + "Cache-Control" : "no-cache", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "28a6ff52-5002-0009-55bf-51c4ef000000", + "Body" : "--batchresponse_80d52b15-e340-4b75-96bf-45f7ef51b9e6\r\nContent-Type: multipart/mixed; boundary=changesetresponse_aaf0fbe7-e8f3-4db4-a19a-e156b86810ca\r\n\r\n--changesetresponse_aaf0fbe7-e8f3-4db4-a19a-e156b86810ca\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 400 Bad Request\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nPreference-Applied: return-no-content\r\nDataServiceVersion: 3.0;\r\nContent-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8\r\n\r\n{\"odata.error\":{\"code\":\"CommandsInBatchActOnDifferentPartitions\",\"message\":{\"lang\":\"en-US\",\"value\":\"1:All commands in a batch must operate on same entity group.\\nRequestId:28a6ff52-5002-0009-55bf-51c4ef000000\\nTime:2021-05-25T23:42:25.5665517Z\"}}}\r\n--changesetresponse_aaf0fbe7-e8f3-4db4-a19a-e156b86810ca--\r\n--batchresponse_80d52b15-e340-4b75-96bf-45f7ef51b9e6--\r\n", + "x-ms-client-request-id" : "75023aaf-6872-466d-8533-6e6f133f27f0", + "Date" : "Tue, 25 May 2021 23:42:25 GMT", + "Content-Type" : "multipart/mixed; boundary=batchresponse_80d52b15-e340-4b75-96bf-45f7ef51b9e6" + }, + "Exception" : null + } ], + "variables" : [ "tablename20677345", "partitionkey884844", "partitionkey24147c", "rowkey5822083fa", "rowkey157589a60" ] +} \ No newline at end of file diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithFailingOperation.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithFailingAction.json similarity index 100% rename from sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithFailingOperation.json rename to sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithFailingAction.json diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithSameRowKeys.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithSameRowKeys.json new file mode 100644 index 0000000000000..0b6a61a8c757e --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/resources/session-records/TablesAsyncClientTest.submitTransactionAsyncWithSameRowKeys.json @@ -0,0 +1,53 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/Tables", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "982167d3-3f74-49a4-9812-ce7c72489b13", + "Content-Type" : "application/json;odata=nometadata" + }, + "Response" : { + "content-length" : "0", + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "204", + "Date" : "Tue, 25 May 2021 23:39:52 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename28690d4f')", + "x-ms-request-id" : "43a756e1-3002-003b-5bbf-519c3f000000", + "x-ms-client-request-id" : "982167d3-3f74-49a4-9812-ce7c72489b13", + "Preference-Applied" : "return-no-content", + "Location" : "https://tablesstoragetests.table.core.windows.net/Tables('tablename28690d4f')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/$batch", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-azure-data-tables/12.0.0-beta.8 (11.0.6; Windows 10; 10.0)", + "x-ms-client-request-id" : "38f195ed-74c6-4ef2-8d29-b277aa2e3230", + "Content-Type" : "multipart/mixed; boundary=batch_663c779a-838d-4fa8-adb6-e5afb083f926" + }, + "Response" : { + "Transfer-Encoding" : "chunked", + "x-ms-version" : "2019-02-02", + "Cache-Control" : "no-cache", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "StatusCode" : "202", + "x-ms-request-id" : "43a7570d-3002-003b-01bf-519c3f000000", + "Body" : "--batchresponse_63930488-8e1b-4239-847a-8d80328d1b36\r\nContent-Type: multipart/mixed; boundary=changesetresponse_5dad98e3-a5c2-40e9-8309-6f55c0ba9cfc\r\n\r\n--changesetresponse_5dad98e3-a5c2-40e9-8309-6f55c0ba9cfc\r\nContent-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\nHTTP/1.1 400 Bad Request\r\nX-Content-Type-Options: nosniff\r\nCache-Control: no-cache\r\nPreference-Applied: return-no-content\r\nDataServiceVersion: 3.0;\r\nContent-Type: application/json;odata=minimalmetadata;streaming=true;charset=utf-8\r\n\r\n{\"odata.error\":{\"code\":\"InvalidDuplicateRow\",\"message\":{\"lang\":\"en-US\",\"value\":\"1:The batch request contains multiple changes with same row key. An entity can appear only once in a batch request.\\nRequestId:43a7570d-3002-003b-01bf-519c3f000000\\nTime:2021-05-25T23:39:53.6225321Z\"}}}\r\n--changesetresponse_5dad98e3-a5c2-40e9-8309-6f55c0ba9cfc--\r\n--batchresponse_63930488-8e1b-4239-847a-8d80328d1b36--\r\n", + "x-ms-client-request-id" : "38f195ed-74c6-4ef2-8d29-b277aa2e3230", + "Date" : "Tue, 25 May 2021 23:39:52 GMT", + "Content-Type" : "multipart/mixed; boundary=batchresponse_63930488-8e1b-4239-847a-8d80328d1b36" + }, + "Exception" : null + } ], + "variables" : [ "tablename28690d4f", "partitionkey03343d", "rowkey360340849" ] +} \ No newline at end of file From 29ffc56cc51dde83263d284ed89696ff1cd07625 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Tue, 25 May 2021 18:56:45 -0500 Subject: [PATCH 07/16] Added transactional batch samples. --- .../tables/TransactionalBatchOperation.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TransactionalBatchOperation.java diff --git a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TransactionalBatchOperation.java b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TransactionalBatchOperation.java new file mode 100644 index 0000000000000..86cc30c843abb --- /dev/null +++ b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TransactionalBatchOperation.java @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.data.tables; + +import com.azure.data.tables.models.TableEntity; +import com.azure.data.tables.models.TableTransactionAction; +import com.azure.data.tables.models.TableTransactionActionType; +import com.azure.data.tables.models.TableTransactionResult; + +import java.util.ArrayList; +import java.util.List; + +/** + * Sample that demonstrates how to create and submit a transactional batch of actions. + */ +public class TransactionalBatchOperation { + /** + * Authenticates with the service and shows how to create and submit a batch of operations. + * + * @param args Unused. Arguments to the program. + */ + public static void main(String[] args) { + // Instantiate a client that will be used to call the service and interact with a given table. + TableClient tableClient = new TableClientBuilder() + .tableName("OfficeSupplies") + .connectionString("") + .buildClient(); + + // Now let's create a list to add transactional batch actions to. + List transactionActions = new ArrayList<>(); + + // Let's create a couple entities. + String partitionKey = "markers4"; + String firstEntityRowKey = "m001"; + String secondEntityRowKey = "m002"; + + TableEntity firstEntity = new TableEntity(partitionKey, firstEntityRowKey) + .addProperty("Brand", "Crayola") + .addProperty("Color", "Red"); + transactionActions.add(new TableTransactionAction(TableTransactionActionType.CREATE, firstEntity)); + System.out.printf("Added create operation to transactional batch for entity with partition key: %s, and row " + + "key: %s\n", partitionKey, firstEntityRowKey); + + TableEntity secondEntity = new TableEntity(partitionKey, secondEntityRowKey) + .addProperty("Brand", "Crayola") + .addProperty("Color", "Blue"); + transactionActions.add(new TableTransactionAction(TableTransactionActionType.CREATE, secondEntity)); + System.out.printf("Added create operation to transactional batch for entity with partition key: %s, and row " + + "key: %s\n", partitionKey, secondEntityRowKey); + + // Now let's update a different entity. + String rowKeyForUpdate = "m003"; + TableEntity entityToUpdate = new TableEntity(partitionKey, rowKeyForUpdate) + .addProperty("Brand", "Crayola") + .addProperty("Color", "Blue"); + transactionActions.add(new TableTransactionAction(TableTransactionActionType.UPDATE_MERGE, entityToUpdate)); + System.out.printf("Added update operation to transactional batch for entity with partition key: %s, and row " + + "key: %s\n", partitionKey, rowKeyForUpdate); + + // And delete another one. + String rowKeyForDelete = "m004"; + TableEntity entityToDelete = new TableEntity(partitionKey, rowKeyForDelete) + .addProperty("Brand", "Crayola") + .addProperty("Color", "Blue"); + transactionActions.add(new TableTransactionAction(TableTransactionActionType.DELETE, entityToDelete)); + System.out.printf("Added delete operation to transactional batch for entity with partition key: %s, and row " + + "key: %s\n", partitionKey, rowKeyForDelete); + + // Finally, let's submit the batch of operations and inspect all the responses. + TableTransactionResult tableTransactionResult = tableClient.submitTransaction(transactionActions); + } +} From b58d047fe5d34621c11d07bcfe520e7cfb2d1597 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Tue, 25 May 2021 19:21:45 -0500 Subject: [PATCH 08/16] Updated TableTransactionAction's JavaDoc to better reflect the delete scenario's behavior. --- .../azure/data/tables/TableAsyncClient.java | 54 +++++++++++-------- .../tables/models/TableTransactionAction.java | 8 ++- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index ac1ad19051c86..768b2e185819e 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -117,7 +117,8 @@ public final class TableAsyncClient { .pipeline(pipeline) .version(serviceVersion.getVersion()) .buildClient(); - this.transactionalBatchImplementation = new TransactionalBatchImpl(tablesImplementation, transactionalBatchSerializer); + this.transactionalBatchImplementation = + new TransactionalBatchImpl(tablesImplementation, transactionalBatchSerializer); this.tableName = tableName; this.pipeline = tablesImplementation.getHttpPipeline(); this.transactionalBatchClient = new TableAsyncClient(this, serviceVersion, tablesSerializer); @@ -364,15 +365,17 @@ Mono> upsertEntityWithResponse(TableEntity entity, TableEntityUpd try { if (updateMode == TableEntityUpdateMode.REPLACE) { - return tablesImplementation.getTables().updateEntityWithResponseAsync(tableName, entity.getPartitionKey(), - entity.getRowKey(), null, null, null, entity.getProperties(), null, context) + return tablesImplementation.getTables() + .updateEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, + null, null, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), null)); } else { - return tablesImplementation.getTables().mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), - entity.getRowKey(), null, null, null, entity.getProperties(), null, context) + return tablesImplementation.getTables() + .mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null + , null, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), @@ -481,15 +484,17 @@ Mono> updateEntityWithResponse(TableEntity entity, TableEntityUpd try { if (updateMode == TableEntityUpdateMode.REPLACE) { - return tablesImplementation.getTables().updateEntityWithResponseAsync(tableName, entity.getPartitionKey(), - entity.getRowKey(), null, null, eTag, entity.getProperties(), null, context) + return tablesImplementation.getTables() + .updateEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, + null, eTag, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), null)); } else { - return tablesImplementation.getTables().mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), - entity.getRowKey(), null, null, eTag, entity.getProperties(), null, context) + return tablesImplementation.getTables() + .mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null + , eTag, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), @@ -597,8 +602,8 @@ Mono> deleteEntityWithResponse(String partitionKey, String rowKey } try { - return tablesImplementation.getTables().deleteEntityWithResponseAsync(tableName, partitionKey, rowKey, matchParam, - null, null, null, context) + return tablesImplementation.getTables().deleteEntityWithResponseAsync(tableName, partitionKey, rowKey, + matchParam, null, null, null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> (Response) new SimpleResponse(response, null)) .onErrorResume(TableServiceException.class, e -> swallowExceptionForStatusCode(404, e, logger)); @@ -1001,15 +1006,16 @@ PagedFlux getAccessPolicy(Context context) { Context finalContext = context; Function>> retriever = marker -> - tablesImplementation.getTables().getAccessPolicyWithResponseAsync(tableName, null, null, finalContext) - .map(response -> new PagedResponseBase<>(response.getRequest(), - response.getStatusCode(), - response.getHeaders(), - response.getValue().stream() - .map(this::toTableSignedIdentifier) - .collect(Collectors.toList()), - null, - response.getDeserializedHeaders())); + tablesImplementation.getTables() + .getAccessPolicyWithResponseAsync(tableName, null, null, finalContext) + .map(response -> new PagedResponseBase<>(response.getRequest(), + response.getStatusCode(), + response.getHeaders(), + response.getValue().stream() + .map(this::toTableSignedIdentifier) + .collect(Collectors.toList()), + null, + response.getDeserializedHeaders())); return new PagedFlux<>(() -> retriever.apply(null), retriever); } catch (RuntimeException e) { @@ -1165,8 +1171,9 @@ Mono> submitTransactionWithResponse(List> submitTransactionWithResponse(List body.addChangeOperation(new TransactionalBatchSubRequest(pair.getT2(), pair.getT1()))) .flatMap(body -> - transactionalBatchImplementation.submitTransactionalBatchWithRestResponseAsync(body, null, finalContext).zipWith(Mono.just(body))) + transactionalBatchImplementation.submitTransactionalBatchWithRestResponseAsync(body, null, + finalContext).zipWith(Mono.just(body))) .flatMap(pair -> parseResponse(pair.getT2(), pair.getT1())) .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java index cd20ff5ab36c6..2b283ec3e3279 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java @@ -14,7 +14,9 @@ public class TableTransactionAction { * Initializes a new instance of the {@link TableTransactionAction}. * * @param actionType The operation type to be applied to the {@code entity}. - * @param entity The table entity to which the {@code actionType} will be applied. + * @param entity The table entity to which the {@code actionType} will be applied. If the an entity's ETag does not + * match during a delete operation, the deletion will not occur and an exception will be thrown. If no ETag is + * present in the {@link TableEntity} in a delete operation, the deletion will occur indiscriminately. */ public TableTransactionAction(TableTransactionActionType actionType, TableEntity entity) { this(actionType, entity, false); @@ -24,7 +26,9 @@ public TableTransactionAction(TableTransactionActionType actionType, TableEntity * Initializes a new instance of the {@link TableTransactionAction}. * * @param actionType The operation type to be applied to the {@code entity}. - * @param entity The table entity to which the {@code actionType} will be applied. + * @param entity The table entity to which the {@code actionType} will be applied. If the an entity's ETag does not + * match during a delete operation, the deletion will not occur and an exception will be thrown. If no ETag is + * present in the {@link TableEntity} in a delete operation, the deletion will occur indiscriminately. * @param ifUnchanged When {@code true}, the ETag of the provided entity must * match the ETag of the entity in the Table service. If the values do not match, the update will not occur and * an exception will be thrown. This value is only applied for update operations. From 7108db4aacc73b10983b389fff0aacb42470ce30 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Tue, 25 May 2021 20:13:27 -0500 Subject: [PATCH 09/16] Fixed CheckStyle issues. --- .../com/azure/data/tables/TableAsyncClient.java | 8 ++++---- .../tables/models/TableTransactionResult.java | 10 ++-------- .../data/tables/TransactionalBatchOperation.java | 16 ++++++++-------- .../azure/data/tables/TablesAsyncClientTest.java | 1 - 4 files changed, 14 insertions(+), 21 deletions(-) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index 768b2e185819e..7c351a640d94e 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -374,8 +374,8 @@ Mono> upsertEntityWithResponse(TableEntity entity, TableEntityUpd null)); } else { return tablesImplementation.getTables() - .mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null - , null, entity.getProperties(), null, context) + .mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null, + null, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), @@ -493,8 +493,8 @@ Mono> updateEntityWithResponse(TableEntity entity, TableEntityUpd null)); } else { return tablesImplementation.getTables() - .mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null - , eTag, entity.getProperties(), null, context) + .mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), null, null, + eTag, entity.getProperties(), null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionResult.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionResult.java index 3c0bfbc7ffa71..ee5aeb2c1f6c1 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionResult.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionResult.java @@ -32,14 +32,8 @@ public TableTransactionResult(List transactionActions, this.transactionActionResponses = transactionActionResponses; this.lookupMap = new HashMap<>(); - if (transactionActions.size() == transactionActionResponses.size()) { - for (int i = 0; i < transactionActions.size(); i++) { - lookupMap.put(transactionActions.get(i).getEntity().getRowKey(), transactionActionResponses.get(i)); - } - - System.out.println("Done creating lookup map"); - } else { - System.out.println("Different sizes WTF."); + for (int i = 0; i < transactionActions.size(); i++) { + lookupMap.put(transactionActions.get(i).getEntity().getRowKey(), transactionActionResponses.get(i)); } } diff --git a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TransactionalBatchOperation.java b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TransactionalBatchOperation.java index 86cc30c843abb..7e489fcd11f36 100644 --- a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TransactionalBatchOperation.java +++ b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TransactionalBatchOperation.java @@ -39,15 +39,15 @@ public static void main(String[] args) { .addProperty("Brand", "Crayola") .addProperty("Color", "Red"); transactionActions.add(new TableTransactionAction(TableTransactionActionType.CREATE, firstEntity)); - System.out.printf("Added create operation to transactional batch for entity with partition key: %s, and row " + - "key: %s\n", partitionKey, firstEntityRowKey); + System.out.printf("Added create operation to transactional batch for entity with partition key: %s, and row " + + "key: %s\n", partitionKey, firstEntityRowKey); TableEntity secondEntity = new TableEntity(partitionKey, secondEntityRowKey) .addProperty("Brand", "Crayola") .addProperty("Color", "Blue"); transactionActions.add(new TableTransactionAction(TableTransactionActionType.CREATE, secondEntity)); - System.out.printf("Added create operation to transactional batch for entity with partition key: %s, and row " + - "key: %s\n", partitionKey, secondEntityRowKey); + System.out.printf("Added create operation to transactional batch for entity with partition key: %s, and row " + + "key: %s\n", partitionKey, secondEntityRowKey); // Now let's update a different entity. String rowKeyForUpdate = "m003"; @@ -55,8 +55,8 @@ public static void main(String[] args) { .addProperty("Brand", "Crayola") .addProperty("Color", "Blue"); transactionActions.add(new TableTransactionAction(TableTransactionActionType.UPDATE_MERGE, entityToUpdate)); - System.out.printf("Added update operation to transactional batch for entity with partition key: %s, and row " + - "key: %s\n", partitionKey, rowKeyForUpdate); + System.out.printf("Added update operation to transactional batch for entity with partition key: %s, and row " + + "key: %s\n", partitionKey, rowKeyForUpdate); // And delete another one. String rowKeyForDelete = "m004"; @@ -64,8 +64,8 @@ public static void main(String[] args) { .addProperty("Brand", "Crayola") .addProperty("Color", "Blue"); transactionActions.add(new TableTransactionAction(TableTransactionActionType.DELETE, entityToDelete)); - System.out.printf("Added delete operation to transactional batch for entity with partition key: %s, and row " + - "key: %s\n", partitionKey, rowKeyForDelete); + System.out.printf("Added delete operation to transactional batch for entity with partition key: %s, and row " + + "key: %s\n", partitionKey, rowKeyForDelete); // Finally, let's submit the batch of operations and inspect all the responses. TableTransactionResult tableTransactionResult = tableClient.submitTransaction(transactionActions); diff --git a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java index 0ee6a09972f33..d4521a98ab300 100644 --- a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java +++ b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java @@ -16,7 +16,6 @@ import com.azure.data.tables.models.ListEntitiesOptions; import com.azure.data.tables.models.TableEntity; import com.azure.data.tables.models.TableEntityUpdateMode; -import com.azure.data.tables.models.TableServiceException; import com.azure.data.tables.models.TableTransactionAction; import com.azure.data.tables.models.TableTransactionActionType; import com.azure.data.tables.models.TableTransactionFailedException; From 5b33a83d5b5c66e1e9d2032361ca6921e67ebc9b Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Thu, 27 May 2021 20:22:24 -0500 Subject: [PATCH 10/16] Updated JavaDoc comments. --- .../com/azure/data/tables/TableAsyncClient.java | 6 +++--- .../java/com/azure/data/tables/TableClient.java | 2 +- .../data/tables/models/TableTransactionAction.java | 14 +++++--------- .../TableServiceAsyncClientCodeSnippets.java | 5 +++-- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index 7c351a640d94e..bbcac7925f9cb 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -430,7 +430,7 @@ public Mono updateEntity(TableEntity entity, TableEntityUpdateMode updateM * * @param entity The entity to update. * @param updateMode The type of update to perform. - * @param ifUnchanged When true, the eTag of the provided entity must match the eTag of the entity in the Table + * @param ifUnchanged When true, the ETag of the provided entity must match the ETag of the entity in the Table * service. If the values do not match, the update will not occur and an exception will be thrown. * * @return An empty reactive result. @@ -455,7 +455,7 @@ public Mono updateEntity(TableEntity entity, TableEntityUpdateMode updateM * * @param entity The entity to update. * @param updateMode The type of update to perform. - * @param ifUnchanged When true, the eTag of the provided entity must match the eTag of the entity in the Table + * @param ifUnchanged When true, the ETag of the provided entity must match the ETag of the entity in the Table * service. If the values do not match, the update will not occur and an exception will be thrown. * * @return A reactive result containing the HTTP response. @@ -580,7 +580,7 @@ public Mono deleteEntity(TableEntity tableEntity) { * * @param partitionKey The partition key of the entity. * @param rowKey The row key of the entity. - * @param eTag The value to compare with the eTag of the entity in the Tables service. If the values do not match, + * @param eTag The value to compare with the ETag of the entity in the Tables service. If the values do not match, * the delete will not occur and an exception will be thrown. * * @return A reactive result containing the response. diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java index e756a0681b388..cf6c0820f00a9 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java @@ -346,7 +346,7 @@ public void deleteEntity(TableEntity tableEntity) { * * @param partitionKey The partition key of the entity. * @param rowKey The row key of the entity. - * @param eTag The value to compare with the eTag of the entity in the Tables service. If the values do not match, + * @param eTag The value to compare with the ETag of the entity in the Tables service. If the values do not match, * the delete will not occur and an exception will be thrown. * @param timeout An optional timeout value beyond which a {@link RuntimeException} will be raised. * @param context Additional context that is passed through the HTTP pipeline during the service call. diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java index 2b283ec3e3279..f88c90fe72084 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java @@ -14,9 +14,7 @@ public class TableTransactionAction { * Initializes a new instance of the {@link TableTransactionAction}. * * @param actionType The operation type to be applied to the {@code entity}. - * @param entity The table entity to which the {@code actionType} will be applied. If the an entity's ETag does not - * match during a delete operation, the deletion will not occur and an exception will be thrown. If no ETag is - * present in the {@link TableEntity} in a delete operation, the deletion will occur indiscriminately. + * @param entity The table entity to which the {@code actionType} will be applied. */ public TableTransactionAction(TableTransactionActionType actionType, TableEntity entity) { this(actionType, entity, false); @@ -26,12 +24,10 @@ public TableTransactionAction(TableTransactionActionType actionType, TableEntity * Initializes a new instance of the {@link TableTransactionAction}. * * @param actionType The operation type to be applied to the {@code entity}. - * @param entity The table entity to which the {@code actionType} will be applied. If the an entity's ETag does not - * match during a delete operation, the deletion will not occur and an exception will be thrown. If no ETag is - * present in the {@link TableEntity} in a delete operation, the deletion will occur indiscriminately. - * @param ifUnchanged When {@code true}, the ETag of the provided entity must - * match the ETag of the entity in the Table service. If the values do not match, the update will not occur and - * an exception will be thrown. This value is only applied for update operations. + * @param entity The table entity to which the {@code actionType} will be applied. + * @param ifUnchanged When {@code true}, the ETag of the provided entity must match the ETag of the entity in the + * Table service. If the values do not match, the action will not be performed and an exception will be thrown. + * This value is only applied for update and delete operations. */ public TableTransactionAction(TableTransactionActionType actionType, TableEntity entity, boolean ifUnchanged) { this.actionType = actionType; diff --git a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceAsyncClientCodeSnippets.java b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceAsyncClientCodeSnippets.java index d27faa53c81bd..fbb20c57d6656 100644 --- a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceAsyncClientCodeSnippets.java +++ b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceAsyncClientCodeSnippets.java @@ -143,8 +143,9 @@ private void update() { System.out.println("Table Entity: " + tableEntity); tableEntity.getProperties().put("Price", "5"); - //UpdateMode.REPLACE: so the entity will be replaced if it exists or the request fails if not found - //ifUnchanged being false means the eTags must not match + // Using TableEntityUpdateMode.REPLACE so the entity will be replaced if it exists or the request fails if + // not found. + // ifUnchanged being false means the ETags must not match. return tableAsyncClient.updateEntity(tableEntity, TableEntityUpdateMode.REPLACE, false); }).subscribe( Void -> { }, From d13712c23a1d64f73b25979a89f633f718b37052 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Thu, 27 May 2021 21:44:57 -0500 Subject: [PATCH 11/16] Changed deleteEntity() and submitTransaction() for deletion and to accept an ifUnchanged flag instead of an eTag for conditional operations. --- .../azure/data/tables/TableAsyncClient.java | 43 ++++++++++--------- .../com/azure/data/tables/TableClient.java | 23 +++++----- .../models/TransactionalBatchAction.java | 33 ++++++-------- .../TableServiceClientCodeSnippets.java | 5 ++- .../data/tables/TablesAsyncClientTest.java | 16 +++---- 5 files changed, 56 insertions(+), 64 deletions(-) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index bbcac7925f9cb..58029096fecd4 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -555,55 +555,56 @@ Mono> deleteTableWithResponse(Context context) { */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono deleteEntity(String partitionKey, String rowKey) { - return deleteEntityWithResponse(partitionKey, rowKey, null).flatMap(FluxUtil::toMono); + return deleteEntityWithResponse(partitionKey, rowKey, null, false, null).flatMap(FluxUtil::toMono); } /** * Deletes an entity from the table. * - * @param tableEntity The table entity to delete. + * @param entity The table entity to delete. + * @param ifUnchanged When true, the ETag of the provided entity must match the ETag of the entity in the Table + * service. If the values do not match, the update will not occur and an exception will be thrown. * - * @return An empty reactive result. + * @return A reactive result containing an HTTP response. * - * @throws IllegalArgumentException If the {@link TableEntity provided entity}'s partition key or row key are - * {@code null} or empty. + * @throws IllegalArgumentException If the provided partition key or row key are {@code null} or empty. * @throws TableServiceException If the request is rejected by the service. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono deleteEntity(TableEntity tableEntity) { - return deleteEntityWithResponse(tableEntity.getPartitionKey(), tableEntity.getRowKey(), tableEntity.getETag(), - null).flatMap(FluxUtil::toMono); + public Mono deleteEntity(TableEntity entity, boolean ifUnchanged) { + return deleteEntityWithResponse(entity, ifUnchanged).flatMap(FluxUtil::toMono); } /** * Deletes an entity from the table. * - * @param partitionKey The partition key of the entity. - * @param rowKey The row key of the entity. - * @param eTag The value to compare with the ETag of the entity in the Tables service. If the values do not match, - * the delete will not occur and an exception will be thrown. + * @param entity The table entity to delete. + * @param ifUnchanged When true, the ETag of the provided entity must match the ETag of the entity in the Table + * service. If the values do not match, the update will not occur and an exception will be thrown. * - * @return A reactive result containing the response. + * @return A reactive result containing an HTTP response. * * @throws IllegalArgumentException If the provided partition key or row key are {@code null} or empty. * @throws TableServiceException If the request is rejected by the service. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono> deleteEntityWithResponse(String partitionKey, String rowKey, String eTag) { - return withContext(context -> deleteEntityWithResponse(partitionKey, rowKey, eTag, context)); + public Mono> deleteEntityWithResponse(TableEntity entity, boolean ifUnchanged) { + return withContext(context -> deleteEntityWithResponse(entity.getPartitionKey(), entity.getRowKey(), + entity.getETag(), ifUnchanged, context)); } - Mono> deleteEntityWithResponse(String partitionKey, String rowKey, String eTag, Context context) { + Mono> deleteEntityWithResponse(String partitionKey, String rowKey, String eTag, boolean ifUnchanged, + Context context) { context = context == null ? Context.NONE : context; - String matchParam = eTag == null ? "*" : eTag; + eTag = ifUnchanged ? eTag : "*"; if (isNullOrEmpty(partitionKey) || isNullOrEmpty(rowKey)) { return monoError(logger, new IllegalArgumentException("'partitionKey' and 'rowKey' cannot be null.")); } try { - return tablesImplementation.getTables().deleteEntityWithResponseAsync(tableName, partitionKey, rowKey, - matchParam, null, null, null, context) + return tablesImplementation.getTables().deleteEntityWithResponseAsync(tableName, partitionKey, rowKey, eTag, + null, null, null, context) .onErrorMap(TableUtils::mapThrowableToTableServiceException) .map(response -> (Response) new SimpleResponse(response, null)) .onErrorResume(TableServiceException.class, e -> swallowExceptionForStatusCode(404, e, logger)); @@ -1172,8 +1173,8 @@ Mono> submitTransactionWithResponse(List deleteEntityWithResponse(String partitionKey, String rowKey, String eTag, Duration timeout, + public Response deleteEntityWithResponse(TableEntity tableEntity, boolean ifUnchanged, Duration timeout, Context context) { - return blockWithOptionalTimeout(client.deleteEntityWithResponse(partitionKey, rowKey, eTag, context), timeout); + return blockWithOptionalTimeout(client.deleteEntityWithResponse(tableEntity.getPartitionKey(), + tableEntity.getRowKey(), tableEntity.getETag(), ifUnchanged, context), timeout); } /** diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchAction.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchAction.java index a9e3e5fbd2250..56ce6679addc9 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchAction.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/implementation/models/TransactionalBatchAction.java @@ -109,39 +109,34 @@ public String toString() { } class DeleteEntity implements TransactionalBatchAction { - private final String partitionKey; - private final String rowKey; - private final String eTag; - - public DeleteEntity(String partitionKey, String rowKey, String eTag) { - this.partitionKey = partitionKey; - this.rowKey = rowKey; - this.eTag = eTag; - } + private final TableEntity entity; + private final boolean ifUnchanged; - public String getPartitionKey() { - return partitionKey; + public DeleteEntity(TableEntity entity, boolean ifUnchanged) { + this.entity = entity; + this.ifUnchanged = ifUnchanged; } - public String getRowKey() { - return rowKey; + public TableEntity getEntity() { + return entity; } - public String getETag() { - return eTag; + public boolean getIfUnchanged() { + return ifUnchanged; } @Override public Mono prepareRequest(TableAsyncClient preparer) { - return preparer.deleteEntityWithResponse(partitionKey, rowKey, eTag).map(Response::getRequest); + return preparer.deleteEntityWithResponse(entity, ifUnchanged).map(Response::getRequest); } @Override public String toString() { return "DeleteEntity{" - + "partitionKey='" + partitionKey + '\'' - + ", rowKey='" + rowKey + '\'' - + ", eTag='" + eTag + '\'' + + "partitionKey='" + entity.getPartitionKey() + '\'' + + ", rowKey='" + entity.getRowKey() + '\'' + + ", eTag='" + entity.getETag() + '\'' + + ", ifUnchanged=" + ifUnchanged + '}'; } } diff --git a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java index 0836803937380..8febd1806001e 100644 --- a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java +++ b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java @@ -111,8 +111,9 @@ private void deleteEntity() { try { TableEntity entity = tableClient.getEntity(partitionKey, rowKey); - //supplying the eTag means the eTags must match to delete - tableClient.deleteEntity(entity); + // Setting ifUnchanged to true means the eTags from the entity must match that of the entity in the Table + // service to delete successfully. + tableClient.deleteEntity(entity, true); } catch (TableServiceException e) { if (e.getValue().getErrorCode() == TableErrorCode.ENTITY_NOT_FOUND) { System.err.println("Delete Entity Unsuccessful. Entity not found."); diff --git a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java index d4521a98ab300..640ed3fce28d0 100644 --- a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java +++ b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/TablesAsyncClientTest.java @@ -365,10 +365,8 @@ void deleteEntityWithResponseAsync() { assertNotNull(createdEntity.getETag(), "'eTag' should not be null."); // Act & Assert - StepVerifier.create(tableClient.deleteEntityWithResponse(partitionKeyValue, rowKeyValue, null)) - .assertNext(response -> { - assertEquals(expectedStatusCode, response.getStatusCode()); - }) + StepVerifier.create(tableClient.deleteEntityWithResponse(createdEntity, false)) + .assertNext(response -> assertEquals(expectedStatusCode, response.getStatusCode())) .expectComplete() .verify(); } @@ -378,10 +376,11 @@ void deleteNonExistingEntityWithResponseAsync() { // Arrange final String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); final String rowKeyValue = testResourceNamer.randomName("rowKey", 20); + final TableEntity entity = new TableEntity(partitionKeyValue, rowKeyValue); final int expectedStatusCode = 404; // Act & Assert - StepVerifier.create(tableClient.deleteEntityWithResponse(partitionKeyValue, rowKeyValue, null)) + StepVerifier.create(tableClient.deleteEntityWithResponse(entity, false)) .assertNext(response -> assertEquals(expectedStatusCode, response.getStatusCode())) .expectComplete() .verify(); @@ -401,11 +400,8 @@ void deleteEntityWithResponseMatchETagAsync() { assertNotNull(createdEntity.getETag(), "'eTag' should not be null."); // Act & Assert - StepVerifier.create(tableClient.deleteEntityWithResponse(partitionKeyValue, rowKeyValue, - createdEntity.getETag())) - .assertNext(response -> { - assertEquals(expectedStatusCode, response.getStatusCode()); - }) + StepVerifier.create(tableClient.deleteEntityWithResponse(createdEntity, true)) + .assertNext(response -> assertEquals(expectedStatusCode, response.getStatusCode())) .expectComplete() .verify(); } From 9848ad66abeaa12e96f70ae7e57adfa841d1171c Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Thu, 27 May 2021 21:59:59 -0500 Subject: [PATCH 12/16] Updated CHANGELOG and fixed JavaDoc issues. --- sdk/tables/azure-data-tables/CHANGELOG.md | 4 ++++ .../data/tables/models/TableTransactionAction.java | 10 +++++----- .../tables/models/TableTransactionActionType.java | 14 ++++++++------ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/sdk/tables/azure-data-tables/CHANGELOG.md b/sdk/tables/azure-data-tables/CHANGELOG.md index f106795e3ef86..f39e000f45c53 100644 --- a/sdk/tables/azure-data-tables/CHANGELOG.md +++ b/sdk/tables/azure-data-tables/CHANGELOG.md @@ -13,6 +13,10 @@ - `TableAsyncClient.submitTransactionWithResponse(List transactionalBatch)` - `TableClient.submitTransaction(List transactionalBatch)` - `TableClient.submitTransactionWithResponse(List transactionalBatch, Duration timeout, Context context)` +- `deleteEntity()` variants in `TableClient` and `TableAsyncClient` now accept an `ifUnchanged` flag instead of an `eTag` parameter for conditional operations. When said flag is set to `true`, the ETag of a given `TableEntity` will be matched with the ETag of the entity in the Table service. +- Removed `deleteEntity(TableEntity tableEntity)` from both `TableClient` and `TableAsyncClient`. +- Replaced `deleteEntityWithResponse(String partitionKey, String rowKey, String eTag)` with `deleteEntity(TableEntity entity, boolean ifUnchanged)` in `TableAsyncClient`. +- Replaced `deleteEntityWithResponse(String partitionKey, String rowKey, String eTag, Duration timeout, Context context)` with `deleteEntity(TableEntity entity, boolean ifUnchanged, Duration timeout, Context context)` in `TableClient`. ## 12.0.0-beta.7 (2021-05-15) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java index f88c90fe72084..05c7db6654aa6 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionAction.java @@ -27,7 +27,7 @@ public TableTransactionAction(TableTransactionActionType actionType, TableEntity * @param entity The table entity to which the {@code actionType} will be applied. * @param ifUnchanged When {@code true}, the ETag of the provided entity must match the ETag of the entity in the * Table service. If the values do not match, the action will not be performed and an exception will be thrown. - * This value is only applied for update and delete operations. + * This value is only applied for update and delete actions. */ public TableTransactionAction(TableTransactionActionType actionType, TableEntity entity, boolean ifUnchanged) { this.actionType = actionType; @@ -47,16 +47,16 @@ public TableTransactionActionType getActionType() { /** * Get the {@link TableEntity table entity} to which the {@code actionType} will be applied. * - * @return The {@link TableEntity table entity} to which the {@code actionType} will be applied + * @return The {@link TableEntity table entity} to which the {@code actionType} will be applied. */ public TableEntity getEntity() { return entity; } /** - * Get the {@code ifUnchanged} value of this action, which indicates if the ETag of the provided entity must - * match the ETag of the entity in the Table service. If the values do not match, the update will not occur and - * an exception will be thrown. This value is only applied for update operations. + * Get the {@code ifUnchanged} value of this action. When {@code true}, the ETag of the provided entity must match + * the ETag of the entity in the Table service. If the values do not match, the action will not be performed + * and an exception will be thrown. This value is only applied for update and delete actions. * * @return The {@code ifUnchanged} value of this action. */ diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java index 340fc5ae9698c..7e96cfdcc4d2c 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java @@ -9,36 +9,38 @@ */ public enum TableTransactionActionType { /** - * Add the entity to the table. This is equivalent to {@link TableClient#createEntity(TableEntity)}. + * Add the entity to the table. This is equivalent to {@code TableClient.createEntity()} or + * {@code TableAsyncClient.createEntity()}. */ CREATE, /** * Upsert the entity in {@link TableEntityUpdateMode#MERGE} mode. This is equivalent to - * {@link TableClient#upsertEntity(TableEntity)}. + * {@code TableClient.upsertEntity()} or {@code TableAsyncClient.upsertEntity()}. */ UPSERT_MERGE, /** * Upsert the entity in {@link TableEntityUpdateMode#REPLACE} mode. This is equivalent to - * {@link TableClient#upsertEntity(TableEntity)}. + * {@code TableClient.upsertEntity()} or {@code TableAsyncClient.upsertEntity()}. */ UPSERT_REPLACE, /** * Update the entity in {@link TableEntityUpdateMode#MERGE} mode. This is equivalent to - * {@link TableClient#updateEntity(TableEntity)}. + * {@code TableClient.updateEntity()} or {@code TableAsyncClient.updateEntity()}. */ UPDATE_MERGE, /** * Update the entity in {@link TableEntityUpdateMode#REPLACE} mode. This is equivalent to - * {@link TableClient#updateEntity(TableEntity)}. + * {@code TableClient.updateEntity()} or {@code TableAsyncClient.updateEntity()}. */ UPDATE_REPLACE, /** - * Delete the entity. This is equivalent to {@link TableClient#deleteEntity(TableEntity)}. + * Delete the entity. This is equivalent to {@code TableClient.deleteEntity()} or + * {@code TableAsyncClient.deleteEntity()}. */ DELETE } From 65746c7827cc9fefc2d1266132beb1bd317e4c36 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Thu, 27 May 2021 22:34:10 -0500 Subject: [PATCH 13/16] Removed unused import. --- .../azure/data/tables/models/TableTransactionActionType.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java index 7e96cfdcc4d2c..eddf9da98350c 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableTransactionActionType.java @@ -2,8 +2,6 @@ // Licensed under the MIT License. package com.azure.data.tables.models; -import com.azure.data.tables.TableClient; - /** * The type of action to be executed on a {@link TableEntity} in a transactional batch operation. */ From 1f26c0523ea7549cd132fe2d5bda1bf4cf26c866 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Fri, 28 May 2021 14:16:49 -0500 Subject: [PATCH 14/16] Applied PR suggestions. --- .../main/java/com/azure/data/tables/TableAsyncClient.java | 6 ++---- .../src/main/java/com/azure/data/tables/TableClient.java | 6 ++---- .../azure/data/tables/TableServiceClientCodeSnippets.java | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index 58029096fecd4..b15b43b180f10 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -562,8 +562,6 @@ public Mono deleteEntity(String partitionKey, String rowKey) { * Deletes an entity from the table. * * @param entity The table entity to delete. - * @param ifUnchanged When true, the ETag of the provided entity must match the ETag of the entity in the Table - * service. If the values do not match, the update will not occur and an exception will be thrown. * * @return A reactive result containing an HTTP response. * @@ -571,8 +569,8 @@ public Mono deleteEntity(String partitionKey, String rowKey) { * @throws TableServiceException If the request is rejected by the service. */ @ServiceMethod(returns = ReturnType.SINGLE) - public Mono deleteEntity(TableEntity entity, boolean ifUnchanged) { - return deleteEntityWithResponse(entity, ifUnchanged).flatMap(FluxUtil::toMono); + public Mono deleteEntity(TableEntity entity) { + return deleteEntityWithResponse(entity, false).flatMap(FluxUtil::toMono); } /** diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java index b5acee523221d..bcc8b9766b7db 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java @@ -329,15 +329,13 @@ public void deleteEntity(String partitionKey, String rowKey) { * Deletes an entity from the table. * * @param tableEntity The table entity to delete. - * @param ifUnchanged When true, the ETag of the provided entity must match the ETag of the entity in the Table - * service. If the values do not match, the update will not occur and an exception will be thrown. * * @throws IllegalArgumentException If the provided partition key or row key are {@code null} or empty. * @throws TableServiceException If the request is rejected by the service. */ @ServiceMethod(returns = ReturnType.SINGLE) - public void deleteEntity(TableEntity tableEntity, boolean ifUnchanged) { - client.deleteEntity(tableEntity, ifUnchanged).block(); + public void deleteEntity(TableEntity tableEntity) { + client.deleteEntity(tableEntity).block(); } /** diff --git a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java index 8febd1806001e..0c340e512de9d 100644 --- a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java +++ b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java @@ -113,7 +113,7 @@ private void deleteEntity() { // Setting ifUnchanged to true means the eTags from the entity must match that of the entity in the Table // service to delete successfully. - tableClient.deleteEntity(entity, true); + tableClient.deleteEntity(entity); } catch (TableServiceException e) { if (e.getValue().getErrorCode() == TableErrorCode.ENTITY_NOT_FOUND) { System.err.println("Delete Entity Unsuccessful. Entity not found."); From e49d7ffaf5a765bec8a39a710bedde464224ab76 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Fri, 28 May 2021 15:25:36 -0500 Subject: [PATCH 15/16] Removed non-WithResponse maximal overloads updateEntity(), upsertEntity() and getEntity(). Updated CHANGELOG. Removed listEntities() and getEntity() overloads that supported TableEntity subclasses in TableAsyncClient. --- sdk/tables/azure-data-tables/CHANGELOG.md | 11 +- .../azure/data/tables/TableAsyncClient.java | 181 +----------------- .../com/azure/data/tables/TableClient.java | 63 ------ .../TableServiceAsyncClientCodeSnippets.java | 11 +- .../TableServiceClientCodeSnippets.java | 10 +- 5 files changed, 22 insertions(+), 254 deletions(-) diff --git a/sdk/tables/azure-data-tables/CHANGELOG.md b/sdk/tables/azure-data-tables/CHANGELOG.md index f39e000f45c53..0412600a6812f 100644 --- a/sdk/tables/azure-data-tables/CHANGELOG.md +++ b/sdk/tables/azure-data-tables/CHANGELOG.md @@ -14,9 +14,14 @@ - `TableClient.submitTransaction(List transactionalBatch)` - `TableClient.submitTransactionWithResponse(List transactionalBatch, Duration timeout, Context context)` - `deleteEntity()` variants in `TableClient` and `TableAsyncClient` now accept an `ifUnchanged` flag instead of an `eTag` parameter for conditional operations. When said flag is set to `true`, the ETag of a given `TableEntity` will be matched with the ETag of the entity in the Table service. -- Removed `deleteEntity(TableEntity tableEntity)` from both `TableClient` and `TableAsyncClient`. -- Replaced `deleteEntityWithResponse(String partitionKey, String rowKey, String eTag)` with `deleteEntity(TableEntity entity, boolean ifUnchanged)` in `TableAsyncClient`. -- Replaced `deleteEntityWithResponse(String partitionKey, String rowKey, String eTag, Duration timeout, Context context)` with `deleteEntity(TableEntity entity, boolean ifUnchanged, Duration timeout, Context context)` in `TableClient`. +- Replaced `deleteEntityWithResponse(String partitionKey, String rowKey, String eTag)` with `deleteEntityWithResponse(TableEntity entity, boolean ifUnchanged)` in `TableAsyncClient`. +- Replaced `deleteEntityWithResponse(String partitionKey, String rowKey, String eTag, Duration timeout, Context context)` with `deleteEntityWithResponse(TableEntity entity, boolean ifUnchanged, Duration timeout, Context context)` in `TableClient`. +- Removed remaining public APIs supporting the use of `TableEntity` subclasses from `TableAsyncClient`. +- Removed the following method overloads from `TableClient` and `TableAsyncClient`: + - `upsertEntity(TableEntity entity, TableEntityUpdateMode updateMode)` + - `updateEntity(TableEntity entity, TableEntityUpdateMode updateMode, + boolean ifUnchanged)` + - `getEntity(String partitionKey, String rowKey, List select)` ## 12.0.0-beta.7 (2021-05-15) diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java index b15b43b180f10..fead6b55b6d71 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableAsyncClient.java @@ -305,30 +305,6 @@ public Mono upsertEntity(TableEntity entity) { return upsertEntityWithResponse(entity, null).flatMap(response -> Mono.justOrEmpty(response.getValue())); } - /** - * Inserts an entity into the table if it does not exist, or updates the existing entity using the specified update - * mode otherwise. - * - * If no entity exists within the table having the same partition key and row key as the provided entity, it will be - * inserted. Otherwise, the existing entity will be updated according to the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to upsert. - * @param updateMode The type of update to perform if the entity already exits. - * - * @return An empty reactive result. - * - * @throws IllegalArgumentException If the provided entity is invalid. - * @throws TableServiceException If the request is rejected by the service. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public Mono upsertEntity(TableEntity entity, TableEntityUpdateMode updateMode) { - return upsertEntityWithResponse(entity, updateMode).flatMap(response -> Mono.justOrEmpty(response.getValue())); - } - /** * Inserts an entity into the table if it does not exist, or updates the existing entity using the specified update * mode otherwise. @@ -418,32 +394,8 @@ public Mono updateEntity(TableEntity entity) { */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono updateEntity(TableEntity entity, TableEntityUpdateMode updateMode) { - return updateEntity(entity, updateMode, false); - } - - /** - * Updates an existing entity using the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to update. - * @param updateMode The type of update to perform. - * @param ifUnchanged When true, the ETag of the provided entity must match the ETag of the entity in the Table - * service. If the values do not match, the update will not occur and an exception will be thrown. - * - * @return An empty reactive result. - * - * @throws IllegalArgumentException If the provided entity is invalid. - * @throws TableServiceException If no entity with the same partition key and row key exists within the table, - * or if {@code ifUnchanged} is {@code true} and the existing entity's eTag does not match that of the provided - * entity. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public Mono updateEntity(TableEntity entity, TableEntityUpdateMode updateMode, boolean ifUnchanged) { - return updateEntityWithResponse(entity, updateMode, ifUnchanged).flatMap(response -> - Mono.justOrEmpty(response.getValue())); + return updateEntityWithResponse(entity, updateMode, false) + .flatMap(response -> Mono.justOrEmpty(response.getValue())); } /** @@ -644,46 +596,6 @@ public PagedFlux listEntities(ListEntitiesOptions options) { token -> withContext(context -> listEntitiesNextPage(token, context, options, TableEntity.class))); } - /** - * Lists all entities within the table. - * - * @param The type of the result value, which must be a subclass of TableEntity. - * @param resultType The type of the result value, which must be a subclass of TableEntity. - * - * @return A paged reactive result containing all entities within the table. - * - * @throws IllegalArgumentException If an instance of the provided {@code resultType} can't be created. - * @throws TableServiceException If the request is rejected by the service. - */ - @ServiceMethod(returns = ReturnType.COLLECTION) - public PagedFlux listEntities(Class resultType) { - return listEntities(new ListEntitiesOptions(), resultType); - } - - /** - * Lists entities using the parameters in the provided options. - * - * If the `filter` parameter in the options is set, only entities matching the filter will be returned. If the - * `select` parameter is set, only the properties included in the select parameter will be returned for each entity. - * If the `top` parameter is set, the number of returned entities will be limited to that value. - * - * @param The type of the result value, which must be a subclass of TableEntity. - * @param options The `filter`, `select`, and `top` OData query options to apply to this operation. - * @param resultType The type of the result value, which must be a subclass of TableEntity. - * - * @return A paged reactive result containing matching entities within the table. - * - * @throws IllegalArgumentException If one or more of the OData query options in {@code options} is malformed, or if - * an instance of the provided {@code resultType} can't be created. - * @throws TableServiceException If the request is rejected by the service. - */ - @ServiceMethod(returns = ReturnType.COLLECTION) - public PagedFlux listEntities(ListEntitiesOptions options, Class resultType) { - return new PagedFlux<>( - () -> withContext(context -> listEntitiesFirstPage(context, options, resultType)), - token -> withContext(context -> listEntitiesNextPage(token, context, options, resultType))); - } - /** * Lists entities using the parameters in the provided options. * @@ -842,68 +754,6 @@ public Mono getEntity(String partitionKey, String rowKey) { return getEntityWithResponse(partitionKey, rowKey, null).flatMap(FluxUtil::toMono); } - /** - * Gets a single entity from the table. - * - * @param partitionKey The partition key of the entity. - * @param rowKey The partition key of the entity. - * @param select A list of properties to select on the entity. - * - * @return A reactive result containing the entity. - * - * @throws IllegalArgumentException If the provided partition key or row key are {@code null} or empty, or if the - * {@code select} OData query option is malformed. - * @throws TableServiceException If no entity with the provided partition key and row key exists within the - * table. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public Mono getEntity(String partitionKey, String rowKey, List select) { - return getEntityWithResponse(partitionKey, rowKey, select).flatMap(FluxUtil::toMono); - } - - /** - * Gets a single entity from the table. - * - * @param The type of the result value, which must be a subclass of TableEntity. - * @param partitionKey The partition key of the entity. - * @param rowKey The partition key of the entity. - * @param resultType The type of the result value, which must be a subclass of TableEntity. - * - * @return A reactive result containing the entity. - * - * @throws IllegalArgumentException If the provided partition key or row key are {@code null} or empty, or if an - * instance of the provided {@code resultType} can't be created. - * @throws TableServiceException If no entity with the provided partition key and row key exists within the - * table. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public Mono getEntity(String partitionKey, String rowKey, Class resultType) { - return getEntityWithResponse(partitionKey, rowKey, null, resultType).flatMap(FluxUtil::toMono); - } - - /** - * Gets a single entity from the table. - * - * @param The type of the result value, which must be a subclass of TableEntity. - * @param partitionKey The partition key of the entity. - * @param rowKey The partition key of the entity. - * @param select An OData `select` expression to limit the set of properties included in the returned entity. - * @param resultType The type of the result value, which must be a subclass of TableEntity. - * - * @return A reactive result containing the entity. - * - * @throws IllegalArgumentException If the provided partition key or row key are {@code null} or empty, if the - * {@code select} OData query option is malformed, or if an instance of the provided {@code resultType} can't be - * created. - * @throws TableServiceException If no entity with the provided partition key and row key exists within the - * table. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public Mono getEntity(String partitionKey, String rowKey, List select, - Class resultType) { - return getEntityWithResponse(partitionKey, rowKey, select, resultType).flatMap(FluxUtil::toMono); - } - /** * Gets a single entity from the table. * @@ -923,29 +773,6 @@ public Mono> getEntityWithResponse(String partitionKey, St return withContext(context -> getEntityWithResponse(partitionKey, rowKey, select, TableEntity.class, context)); } - /** - * Gets a single entity from the table. - * - * @param The type of the result value, which must be a subclass of TableEntity. - * @param partitionKey The partition key of the entity. - * @param rowKey The partition key of the entity. - * @param select An OData `select` expression to limit the set of properties included in the returned entity. - * @param resultType The type of the result value, which must be a subclass of TableEntity. - * - * @return A reactive result containing the response and entity. - * - * @throws IllegalArgumentException If the provided partition key or row key are {@code null} or empty, if the - * {@code select} OData query option is malformed, or if an instance of the provided {@code resultType} can't be - * created. - * @throws TableServiceException If no entity with the provided partition key and row key exists within the - * table. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public Mono> getEntityWithResponse(String partitionKey, String rowKey, - List select, Class resultType) { - return withContext(context -> getEntityWithResponse(partitionKey, rowKey, select, resultType, context)); - } - Mono> getEntityWithResponse(String partitionKey, String rowKey, List select, Class resultType, Context context) { @@ -1105,7 +932,7 @@ private AccessPolicy toAccessPolicy(TableAccessPolicy tableAccessPolicy) { * fail. */ @ServiceMethod(returns = ReturnType.SINGLE) - public synchronized Mono submitTransaction(List transactionActions) { + public Mono submitTransaction(List transactionActions) { return submitTransactionWithResponse(transactionActions) .flatMap(response -> Mono.justOrEmpty(response.getValue())); } @@ -1129,7 +956,7 @@ public synchronized Mono submitTransaction(List> submitTransactionWithResponse(List transactionActions) { + public Mono> submitTransactionWithResponse(List transactionActions) { return withContext(context -> submitTransactionWithResponse(transactionActions, context)); } diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java index bcc8b9766b7db..f811b2ee05e4f 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/TableClient.java @@ -155,28 +155,6 @@ public void upsertEntity(TableEntity entity) { client.upsertEntity(entity).block(); } - /** - * Inserts an entity into the table if it does not exist, or updates the existing entity using the specified update - * mode otherwise. - * - * If no entity exists within the table having the same partition key and row key as the provided entity, it will be - * inserted. Otherwise, the existing entity will be updated according to the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to upsert. - * @param updateMode The type of update to perform if the entity already exits. - * - * @throws IllegalArgumentException If the provided entity is invalid. - * @throws TableServiceException If the request is rejected by the service. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public void upsertEntity(TableEntity entity, TableEntityUpdateMode updateMode) { - client.upsertEntity(entity, updateMode).block(); - } - /** * Inserts an entity into the table if it does not exist, or updates the existing entity using the specified update * mode otherwise. @@ -235,28 +213,6 @@ public void updateEntity(TableEntity entity, TableEntityUpdateMode updateMode) { client.updateEntity(entity, updateMode).block(); } - /** - * Updates an existing entity using the specified update mode. - * - * When the update mode is 'MERGE', the provided entity's properties will be merged into the existing entity. When - * the update mode is 'REPLACE', the provided entity's properties will completely replace those in the existing - * entity. - * - * @param entity The entity to update. - * @param updateMode The type of update to perform. - * @param ifUnchanged When true, the eTag of the provided entity must match the eTag of the entity in the Table - * service. If the values do not match, the update will not occur and an exception will be thrown. - * - * @throws IllegalArgumentException If the provided entity is invalid. - * @throws TableServiceException If no entity with the same partition key and row key exists within the table, - * or if {@code ifUnchanged} is {@code true} and the existing entity's eTag does not match that of the provided - * entity. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public void updateEntity(TableEntity entity, TableEntityUpdateMode updateMode, boolean ifUnchanged) { - client.updateEntity(entity, updateMode, ifUnchanged).block(); - } - /** * Updates an existing entity using the specified update mode. * @@ -409,25 +365,6 @@ public TableEntity getEntity(String partitionKey, String rowKey) { return client.getEntity(partitionKey, rowKey).block(); } - /** - * Gets a single entity from the table. - * - * @param partitionKey The partition key of the entity. - * @param rowKey The partition key of the entity. - * @param select A list of properties to select on the entity. - * - * @return The entity. - * - * @throws IllegalArgumentException If the provided partition key or row key are {@code null} or empty, or if the - * {@code select} OData query option is malformed. - * @throws TableServiceException If no entity with the provided partition key and row key exists within the - * table. - */ - @ServiceMethod(returns = ReturnType.SINGLE) - public TableEntity getEntity(String partitionKey, String rowKey, List select) { - return client.getEntity(partitionKey, rowKey, select).block(); - } - /** * Gets a single entity from the table. * diff --git a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceAsyncClientCodeSnippets.java b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceAsyncClientCodeSnippets.java index fbb20c57d6656..db80e07df2e28 100644 --- a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceAsyncClientCodeSnippets.java +++ b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceAsyncClientCodeSnippets.java @@ -118,8 +118,8 @@ private void upsert() { System.out.println("Table Entity: " + tableEntity); tableEntity.getProperties().put("Price", "5"); - //default is for UpdateMode is UpdateMode.MERGE, which means it merges if exists; inserts if not - //ifUnchanged being true means the eTags must match to upsert + // The default TableEntityUpdateMode for this operation is MERGE, which means it merges the entities if one + // already exists or inserts it if it does not. return tableAsyncClient.upsertEntity(tableEntity); }).subscribe( Void -> { }, @@ -143,10 +143,9 @@ private void update() { System.out.println("Table Entity: " + tableEntity); tableEntity.getProperties().put("Price", "5"); - // Using TableEntityUpdateMode.REPLACE so the entity will be replaced if it exists or the request fails if - // not found. - // ifUnchanged being false means the ETags must not match. - return tableAsyncClient.updateEntity(tableEntity, TableEntityUpdateMode.REPLACE, false); + // The default TableEntityUpdateMode for this operation is REPLACE, which means the entity will be + // replaced if it exists or the request will fail if not found. + return tableAsyncClient.updateEntity(tableEntity, TableEntityUpdateMode.REPLACE); }).subscribe( Void -> { }, error -> System.err.println("There was an error updating the Entity. Error: " + error), diff --git a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java index 0c340e512de9d..779a0ffa41cb8 100644 --- a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java +++ b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java @@ -137,8 +137,8 @@ private void updateEntity() { try { TableEntity entity = tableClient.getEntity(partitionKey, rowKey); - //default is for UpdateMode is UpdateMode.MERGE, which means it merges if exists; fails if not - //ifUnchanged being false means the eTags must not match + // The default TableEntityUpdateMode for this operation is MERGE, which means it merges the entities if one + // already exists or inserts it if it does not. tableClient.updateEntity(entity); } catch (TableServiceException e) { if (e.getValue().getErrorCode() == TableErrorCode.ENTITY_NOT_FOUND) { @@ -163,9 +163,9 @@ private void upsertEntity() { try { TableEntity entity = tableClient.getEntity(partitionKey, rowKey); - //default is for UpdateMode is UpdateMode.REPLACE, which means it replaces if exists; inserts if not - //always upsert because if no ifUnchanged boolean present the "*" in request. - tableClient.upsertEntity(entity, TableEntityUpdateMode.REPLACE); + // The default TableEntityUpdateMode is REPLACE, which means the entity will be replaced if it exists or + // the request fails if not found. + tableClient.upsertEntity(entity); } catch (TableServiceException e) { if (e.getValue().getErrorCode() == TableErrorCode.ENTITY_NOT_FOUND) { System.err.println("Cannot find entity. Upsert unsuccessful"); From ac513ebb49d458db631b0c59903ef392e9ebca75 Mon Sep 17 00:00:00 2001 From: Victor Colin Amador Date: Fri, 28 May 2021 17:18:32 -0500 Subject: [PATCH 16/16] Removed unused import. --- .../com/azure/data/tables/TableServiceClientCodeSnippets.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java index 779a0ffa41cb8..53820a8b9f463 100644 --- a/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java +++ b/sdk/tables/azure-data-tables/src/samples/java/com/azure/data/tables/TableServiceClientCodeSnippets.java @@ -7,7 +7,6 @@ import com.azure.data.tables.models.ListEntitiesOptions; import com.azure.data.tables.models.ListTablesOptions; import com.azure.data.tables.models.TableEntity; -import com.azure.data.tables.models.TableEntityUpdateMode; import com.azure.data.tables.models.TableErrorCode; import com.azure.data.tables.models.TableItem; import com.azure.data.tables.models.TableServiceException;