diff --git a/sdk/tables/azure-data-tables/CHANGELOG.md b/sdk/tables/azure-data-tables/CHANGELOG.md index 7232466e04f73..3de53a69d815c 100644 --- a/sdk/tables/azure-data-tables/CHANGELOG.md +++ b/sdk/tables/azure-data-tables/CHANGELOG.md @@ -2,23 +2,34 @@ ## 12.0.0-beta.2 (Unreleased) +### Added + +- Developers can now subclass `TableEntity` and decorate the subclass with properties, rather than adding properties + manually by calling `addProperty()`. Client methods that perform read operations now accept an additional parameter + `resultType` to return the result of the read operation as the specified type instead of always returning + `TableEntity` instances. Client methods that perform write operations accept subclasses of `TableEntity` in addition + to instances of the base class itself. [#13692](https://github.com/azure/azure-sdk-for-java/issues/13692) + ### Changed -- The `getEntity` methods have gained the `select` query option to allow for more efficient existence checks for a table entity [#15289](https://github.com/Azure/azure-sdk-for-java/issues/15289) +- The `getEntity` methods have gained the `select` query option to allow for more efficient existence checks for a table + entity. [#15289](https://github.com/Azure/azure-sdk-for-java/issues/15289) +- The non-functional `TableClient.listEntities(options, timeout)` method was removed. ### Fixed -- Can Not Create TableClientBuilder [#15294](https://github.com/Azure/azure-sdk-for-java/issues/15294) -- Missing module-info.java [#15296](https://github.com/Azure/azure-sdk-for-java/issues/15296) -- The `TableClient.updateEntity(entity)` method was mistakenly performing an upsert operation rather than an update -- The `TableAsyncClient.updateEntity(entity)` method always returned an empty result -- The non-functional `TableClient.listEntities(options, timeout)` method was removed +- TableClientBuilder's constructor was mistakenly hidden from the public API. + [#15294](https://github.com/Azure/azure-sdk-for-java/issues/15294) +- The library was missing a module-info.java. [#15296](https://github.com/Azure/azure-sdk-for-java/issues/15296) +- The `TableClient.updateEntity(entity)` method was mistakenly performing an upsert operation rather than an update. +- The `TableAsyncClient.updateEntity(entity)` method always returned an empty result. ## 12.0.0-beta.1 (2020-09-10): Version 12.0.0-beta.1 is a beta of our efforts in creating a client library that is developer-friendly, idiomatic to the Java ecosystem, and as consistent across different languages and platforms as possible. The principles that guide -our efforts can be found in the [Azure SDK Design Guidelines for Java](https://azure.github.io/azure-sdk/java_introduction.html). +our efforts can be found in the +[Azure SDK Design Guidelines for Java](https://azure.github.io/azure-sdk/java_introduction.html). ### Features diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/EntityHelper.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/EntityHelper.java new file mode 100644 index 0000000000000..b9923311f90e7 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/EntityHelper.java @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables; + +import com.azure.core.util.logging.ClientLogger; +import com.azure.data.tables.models.TableEntity; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; +import java.util.stream.Collectors; + +final class EntityHelper { + private static final HashSet TABLE_ENTITY_METHODS = Arrays.stream(TableEntity.class.getMethods()) + .map(Method::getName).collect(Collectors.toCollection(HashSet::new)); + + private EntityHelper() { + } + + // Given a subclass of `TableEntity`, locate all getter methods (those that start with `get` or `is`, take no + // parameters, and produce a non-void value) and add their values to the properties map + static void setPropertiesFromGetters(TableEntity entity, ClientLogger logger) { + Class myClass = entity.getClass(); + + // Do nothing if the entity is actually a `TableEntity` rather than a subclass + if (myClass == TableEntity.class) { + return; + } + + for (Method m : myClass.getMethods()) { + // Skip any non-getter methods + if (m.getName().length() < 3 + || TABLE_ENTITY_METHODS.contains(m.getName()) + || (!m.getName().startsWith("get") && !m.getName().startsWith("is")) + || m.getParameterTypes().length != 0 + || void.class.equals(m.getReturnType())) { + continue; + } + + // A method starting with `is` is only a getter if it returns a boolean + if (m.getName().startsWith("is") && m.getReturnType() != Boolean.class + && m.getReturnType() != boolean.class) { + continue; + } + + // Remove the `get` or `is` prefix to get the name of the property + int prefixLength = m.getName().startsWith("is") ? 2 : 3; + String propName = m.getName().substring(prefixLength); + + try { + // Invoke the getter and store the value in the properties map + entity.getProperties().put(propName, m.invoke(entity)); + } catch (ReflectiveOperationException | IllegalArgumentException e) { + logger.logThrowableAsWarning(new ReflectiveOperationException(String.format( + "Failed to get property '%s' on type '%s'", propName, myClass.getName()), e)); + } + } + } + + @SuppressWarnings("unchecked") + static T convertToSubclass(TableEntity entity, Class clazz, ClientLogger logger) { + // Do nothing if the entity is actually a `TableEntity` rather than a subclass + if (TableEntity.class == clazz) { + return (T) entity; + } + + T result; + try { + // Create a new instance of the provided `TableEntity` subclass by calling its two-argument constructor that + // accepts the partitionKey and rowKey. If the developer implemented their own custom constructor instead, + // this will fail. + result = clazz.getDeclaredConstructor(String.class, String.class).newInstance(entity.getPartitionKey(), + entity.getRowKey()); + } catch (ReflectiveOperationException | SecurityException e) { + throw logger.logExceptionAsError(new IllegalArgumentException(String.format( + "Failed to instantiate type '%s'. It must contain a constructor that accepts two arguments: " + + "the partition key and row key.", clazz.getName()), e)); + } + + // Copy all of the properties from the provided `TableEntity` into the new instance + result.addProperties(entity.getProperties()); + + for (Method m : clazz.getMethods()) { + // Skip any non-setter methods + if (m.getName().length() < 4 + || !m.getName().startsWith("set") + || m.getParameterTypes().length != 1 + || !void.class.equals(m.getReturnType())) { + continue; + } + + // Remove the `set` prefix to get the name of the property + String propName = m.getName().substring(3); + + // Skip this setter if the properties map doesn't contain a matching property + Object value = result.getProperties().get(propName); + if (value == null) { + continue; + } + + // If the setter accepts an enum parameter and the property's value is a string, attempt to convert the + // value to an instance of that enum type. Enums are serialized as strings using their 'name' which is the + // string representation of the enum value, regardless of whether they contain associated values or whether + // their `toString` method has been overridden by the developer. + Class paramType = m.getParameterTypes()[0]; + if (paramType.isEnum() && value instanceof String) { + try { + value = Enum.valueOf(paramType.asSubclass(Enum.class), (String) value); + } catch (IllegalArgumentException e) { + logger.logThrowableAsWarning(new IllegalArgumentException(String.format( + "Failed to convert '%s' to value of enum '%s'", propName, paramType.getName()), e)); + continue; + } + } + + try { + // Invoke the setter with the value of the property + m.invoke(result, value); + } catch (ReflectiveOperationException | IllegalArgumentException e) { + logger.logThrowableAsWarning(new ReflectiveOperationException(String.format( + "Failed to set property '%s' on type '%s'", propName, clazz.getName()), e)); + } + } + + return result; + } +} 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 3fab41dc6647b..5d7d0d14fecb6 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 @@ -176,6 +176,10 @@ public Mono> createEntityWithResponse(TableEntity entity) { } Mono> createEntityWithResponse(TableEntity entity, Context context) { + if (entity == null) { + return monoError(logger, new NullPointerException("TableEntity cannot be null")); + } + EntityHelper.setPropertiesFromGetters(entity, logger); return implementation.getTables().insertEntityWithResponseAsync(tableName, null, null, ResponseFormat.RETURN_NO_CONTENT, entity.getProperties(), null, context).map(response -> { @@ -228,6 +232,7 @@ Mono> upsertEntityWithResponse(TableEntity entity, UpdateMode upd if (entity == null) { return monoError(logger, new NullPointerException("TableEntity cannot be null")); } + EntityHelper.setPropertiesFromGetters(entity, logger); if (updateMode == UpdateMode.REPLACE) { return implementation.getTables().updateEntityWithResponseAsync(tableName, entity.getPartitionKey(), entity.getRowKey(), timeoutInt, null, "*", @@ -314,6 +319,10 @@ public Mono> updateEntityWithResponse(TableEntity entity, boolean Mono> updateEntityWithResponse(TableEntity entity, boolean ifUnchanged, UpdateMode updateMode, Duration timeout, Context context) { Integer timeoutInt = timeout == null ? null : (int) timeout.getSeconds(); + if (entity == null) { + return monoError(logger, new NullPointerException("TableEntity cannot be null")); + } + EntityHelper.setPropertiesFromGetters(entity, logger); if (updateMode == null || updateMode == UpdateMode.MERGE) { if (ifUnchanged) { return implementation.getTables().mergeEntityWithResponseAsync(tableName, entity.getPartitionKey(), @@ -463,27 +472,52 @@ public PagedFlux listEntities() { @ServiceMethod(returns = ReturnType.COLLECTION) public PagedFlux listEntities(ListEntitiesOptions options) { return new PagedFlux<>( - () -> withContext(context -> listEntitiesFirstPage(context, options)), - token -> withContext(context -> listEntitiesNextPage(token, context, options))); - } //802 + () -> withContext(context -> listEntitiesFirstPage(context, options, TableEntity.class)), + token -> withContext(context -> listEntitiesNextPage(token, context, options, TableEntity.class))); + } - PagedFlux listEntities(ListEntitiesOptions options, Context context) { + /** + * Queries and returns entities in the given table using the odata query options + * + * @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 flux of all the entities which fit this criteria + */ + @ServiceMethod(returns = ReturnType.COLLECTION) + public PagedFlux listEntities(Class resultType) { + return listEntities(new ListEntitiesOptions(), resultType); + } + /** + * Queries and returns entities in the given table using the odata query options + * + * @param the type of the result value, which must be a subclass of TableEntity + * @param options the odata query object + * @param resultType the type of the result value, which must be a subclass of TableEntity + * + * @return a paged flux of all the entities which fit this criteria + */ + @ServiceMethod(returns = ReturnType.COLLECTION) + public PagedFlux listEntities(ListEntitiesOptions options, Class resultType) { return new PagedFlux<>( - () -> listEntitiesFirstPage(context, options), - token -> listEntitiesNextPage(token, context, options)); - } //802 + () -> withContext(context -> listEntitiesFirstPage(context, options, resultType)), + token -> withContext(context -> listEntitiesNextPage(token, context, options, resultType))); + } - private Mono> listEntitiesFirstPage(Context context, ListEntitiesOptions options) { + private Mono> listEntitiesFirstPage(Context context, + ListEntitiesOptions options, + Class resultType) { try { - return listEntities(null, null, context, options); + return listEntities(null, null, context, options, resultType); } catch (RuntimeException e) { return monoError(logger, e); } - } //1459 + } - private Mono> listEntitiesNextPage(String token, Context context, - ListEntitiesOptions options) { + private Mono> listEntitiesNextPage(String token, Context context, + ListEntitiesOptions options, + Class resultType) { if (token == null) { return Mono.empty(); } @@ -495,14 +529,15 @@ private Mono> listEntitiesNextPage(String token, Cont } String nextPartitionKey = split[0]; String nextRowKey = split[1]; - return listEntities(nextPartitionKey, nextRowKey, context, options); + return listEntities(nextPartitionKey, nextRowKey, context, options, resultType); } catch (RuntimeException e) { return monoError(logger, e); } - } //1459 + } - private Mono> listEntities(String nextPartitionKey, String nextRowKey, Context context, - ListEntitiesOptions options) { + private Mono> listEntities(String nextPartitionKey, String nextRowKey, + Context context, ListEntitiesOptions options, + Class resultType) { QueryOptions queryOptions = new QueryOptions() .setFilter(options.getFilter()) .setTop(options.getTop()) @@ -521,23 +556,24 @@ private Mono> listEntities(String nextPartitionKey, S return Mono.empty(); } - final List entities = entityResponseValue.stream() + final List entities = entityResponseValue.stream() .map(ModelHelper::createEntity) + .map(e -> EntityHelper.convertToSubclass(e, resultType, logger)) .collect(Collectors.toList()); - return Mono.just(new EntityPaged(response, entities, + return Mono.just(new EntityPaged<>(response, entities, response.getDeserializedHeaders().getXMsContinuationNextPartitionKey(), response.getDeserializedHeaders().getXMsContinuationNextRowKey())); }); - } //1836 + } - private static class EntityPaged implements PagedResponse { + private static class EntityPaged implements PagedResponse { private final Response httpResponse; - private final IterableStream entityStream; + private final IterableStream entityStream; private final String continuationToken; - EntityPaged(Response httpResponse, List entityList, + EntityPaged(Response httpResponse, List entityList, String nextPartitionKey, String nextRowKey) { if (nextPartitionKey == null || nextRowKey == null) { this.continuationToken = null; @@ -564,7 +600,7 @@ public HttpRequest getRequest() { } @Override - public IterableStream getElements() { + public IterableStream getElements() { return entityStream; } @@ -606,6 +642,39 @@ public Mono getEntity(String partitionKey, String rowKey, String se return getEntityWithResponse(partitionKey, rowKey, select).flatMap(FluxUtil::toMono); } + /** + * gets the entity which fits the given criteria + * + * @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 row key of the entity + * @param resultType the type of the result value, which must be a subclass of TableEntity + * + * @return a mono of the table entity subclass specified in resultType + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getEntity(String partitionKey, String rowKey, Class resultType) { + return getEntityWithResponse(partitionKey, rowKey, null, resultType).flatMap(FluxUtil::toMono); + } + + /** + * gets the entity which fits the given criteria + * + * @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 row key of the entity + * @param select a select expression using OData notation. Limits the columns on each record to just those + * requested, e.g. "$select=PolicyAssignmentId, ResourceId". + * @param resultType the type of the result value, which must be a subclass of TableEntity + * + * @return a mono of the table entity subclass specified in resultType + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono getEntity(String partitionKey, String rowKey, String select, + Class resultType) { + return getEntityWithResponse(partitionKey, rowKey, select, resultType).flatMap(FluxUtil::toMono); + } + /** * gets the entity which fits the given criteria * @@ -618,11 +687,31 @@ public Mono getEntity(String partitionKey, String rowKey, String se */ @ServiceMethod(returns = ReturnType.SINGLE) public Mono> getEntityWithResponse(String partitionKey, String rowKey, String select) { - return withContext(context -> getEntityWithResponse(partitionKey, rowKey, select, null, context)); + return withContext(context -> getEntityWithResponse(partitionKey, rowKey, select, TableEntity.class, null, + context)); + } + + /** + * gets the entity which fits the given criteria + * + * @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 row key of the entity + * @param select a select expression using OData notation. Limits the columns on each record to just those + * requested, e.g. "$select=PolicyAssignmentId, ResourceId". + * @param resultType the type of the result value, which must be a subclass of TableEntity + * + * @return a mono of the response with the table entity subclass specified in resultType + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public Mono> getEntityWithResponse(String partitionKey, String rowKey, + String select, Class resultType) { + return withContext(context -> getEntityWithResponse(partitionKey, rowKey, select, resultType, null, context)); } - Mono> getEntityWithResponse(String partitionKey, String rowKey, String select, - Duration timeout, Context context) { + Mono> getEntityWithResponse(String partitionKey, String rowKey, String select, + Class resultType, Duration timeout, + Context context) { Integer timeoutInt = timeout == null ? null : (int) timeout.getSeconds(); QueryOptions queryOptions = new QueryOptions() .setFormat(OdataMetadataFormat.APPLICATION_JSON_ODATA_FULLMETADATA); @@ -659,7 +748,7 @@ Mono> getEntityWithResponse(String partitionKey, String ro // TODO: Potentially update logic to deserialize them all. final TableEntity entity = ModelHelper.createEntity(matchingEntities.get(0)); sink.next(new SimpleResponse<>(response.getRequest(), response.getStatusCode(), response.getHeaders(), - entity)); + EntityHelper.convertToSubclass(entity, resultType, logger))); }); } } 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 226056b570b96..10765d10f38f1 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 @@ -11,6 +11,7 @@ import com.azure.data.tables.models.ListEntitiesOptions; import com.azure.data.tables.models.TableEntity; import com.azure.data.tables.models.UpdateMode; + import java.time.Duration; /** @@ -328,7 +329,7 @@ public Response deleteEntityWithResponse(String partitionKey, String rowKe /** * Queries and returns all entities in the given table * - * @return a list of the tables that fit the query + * @return a list of all the entities which fit this criteria */ @ServiceMethod(returns = ReturnType.COLLECTION) public PagedIterable listEntities() { @@ -339,13 +340,38 @@ public PagedIterable listEntities() { * Queries and returns entities in the given table using the odata QueryOptions * * @param options the odata query object - * @return a list of the tables that fit the query + * @return a list of all the entities which fit this criteria */ @ServiceMethod(returns = ReturnType.COLLECTION) public PagedIterable listEntities(ListEntitiesOptions options) { return new PagedIterable<>(client.listEntities(options)); } + /** + * Queries and returns all entities in the given 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 list of all the entities which fit this criteria + */ + @ServiceMethod(returns = ReturnType.COLLECTION) + public PagedIterable listEntities(Class resultType) { + return new PagedIterable<>(client.listEntities(resultType)); + } + + /** + * Queries and returns entities in the given table using the odata QueryOptions + * + * @param the type of the result value, which must be a subclass of TableEntity + * @param options the odata query object + * @param resultType the type of the result value, which must be a subclass of TableEntity + * @return a list of all the entities which fit this criteria + */ + @ServiceMethod(returns = ReturnType.COLLECTION) + public PagedIterable listEntities(ListEntitiesOptions options, Class resultType) { + return new PagedIterable<>(client.listEntities(options, resultType)); + } + /** * gets the entity which fits the given criteria * @@ -372,6 +398,38 @@ public TableEntity getEntity(String partitionKey, String rowKey, String select) return client.getEntity(partitionKey, rowKey, select).block(); } + /** + * gets the entity which fits the given criteria + * + * @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 row key of the entity + * @param resultType the type of the result value, which must be a subclass of TableEntity + * + * @return the table entity subclass specified in resultType + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public T getEntity(String partitionKey, String rowKey, Class resultType) { + return client.getEntity(partitionKey, rowKey, resultType).block(); + } + + /** + * gets the entity which fits the given criteria + * + * @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 row key of the entity + * @param select a select expression using OData notation. Limits the columns on each record to just those + * requested, e.g. "$select=PolicyAssignmentId, ResourceId". + * @param resultType the type of the result value, which must be a subclass of TableEntity + * + * @return the table entity subclass specified in resultType + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public T getEntity(String partitionKey, String rowKey, String select, Class resultType) { + return client.getEntity(partitionKey, rowKey, select, resultType).block(); + } + /** * gets the entity which fits the given criteria * @@ -384,24 +442,45 @@ public TableEntity getEntity(String partitionKey, String rowKey, String select) */ @ServiceMethod(returns = ReturnType.SINGLE) public TableEntity getEntity(String partitionKey, String rowKey, String select, Duration timeout) { - return getEntityWithResponse(partitionKey, rowKey, select, timeout, null).getValue(); + return getEntityWithResponse(partitionKey, rowKey, select, TableEntity.class, timeout, null).getValue(); + } + + /** + * gets the entity which fits the given criteria + * + * @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 row key of the entity + * @param select a select expression using OData notation. Limits the columns on each record to just those + * requested, e.g. "$select=PolicyAssignmentId, ResourceId". + * @param resultType the type of the result value, which must be a subclass of TableEntity + * @param timeout max time for query to execute before erroring out + * @return the table entity + */ + @ServiceMethod(returns = ReturnType.SINGLE) + public T getEntity(String partitionKey, String rowKey, String select, Class resultType, + Duration timeout) { + return getEntityWithResponse(partitionKey, rowKey, select, resultType, timeout, null).getValue(); } /** * gets the entity which fits the given criteria * + * @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 row key of the entity * @param select a select expression using OData notation. Limits the columns on each record to just those * requested, e.g. "$select=PolicyAssignmentId, ResourceId". + * @param resultType the type of the result value, which must be a subclass of TableEntity * @param timeout max time for query to execute before erroring out * @param context the context of the query * @return a mono of the response with the table entity */ @ServiceMethod(returns = ReturnType.SINGLE) - public Response getEntityWithResponse(String partitionKey, String rowKey, String select, - Duration timeout, Context context) { - return client.getEntityWithResponse(partitionKey, rowKey, select, timeout, context).block(); + public Response getEntityWithResponse(String partitionKey, String rowKey, String select, + Class resultType, Duration timeout, + Context context) { + return client.getEntityWithResponse(partitionKey, rowKey, select, resultType, timeout, context).block(); } } 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 3927adbc5b2b3..63cbf148caa67 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 @@ -10,12 +10,13 @@ import java.util.Map; import java.util.Objects; import java.util.function.Function; +import java.util.function.Supplier; /** * Used to access internal methods on models. */ public final class ModelHelper { - private static Function, TableEntity> entityCreator; + private static Supplier entityCreator; private static Function itemCreator; static { @@ -34,7 +35,7 @@ public final class ModelHelper { * @param creator The entity creator. * @throws IllegalStateException if the creator has already been set. */ - public static void setEntityCreator(Function, TableEntity> creator) { + public static void setEntityCreator(Supplier creator) { Objects.requireNonNull(creator, "'creator' cannot be null."); if (ModelHelper.entityCreator != null) { @@ -74,7 +75,7 @@ public static TableEntity createEntity(Map properties) { new IllegalStateException("'entityCreator' should not be null.")); } - return entityCreator.apply(properties); + return entityCreator.get().addProperties(properties); } /** diff --git a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableEntity.java b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableEntity.java index 74f99ff5a78a2..876a7ca3d5b2f 100644 --- a/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableEntity.java +++ b/sdk/tables/azure-data-tables/src/main/java/com/azure/data/tables/models/TableEntity.java @@ -5,8 +5,8 @@ import com.azure.core.annotation.Fluent; import com.azure.core.util.logging.ClientLogger; -import com.azure.data.tables.implementation.TablesConstants; import com.azure.data.tables.implementation.ModelHelper; +import com.azure.data.tables.implementation.TablesConstants; import java.time.OffsetDateTime; import java.util.HashMap; @@ -19,16 +19,8 @@ @Fluent public class TableEntity { private final ClientLogger logger = new ClientLogger(TableEntity.class); - private final String partitionKey; - private final String rowKey; private final Map properties; - private final OffsetDateTime timestamp; - private final String eTag; - private final String odataType; - private final String odataId; - private final String odataEditLink; - static { // This is used by classes in different packages to get access to private and package-private methods. ModelHelper.setEntityCreator(TableEntity::new); @@ -45,76 +37,29 @@ public TableEntity(String partitionKey, String rowKey) { throw logger.logExceptionAsError(new IllegalArgumentException(String.format( "'%s' is an empty value.", TablesConstants.PARTITION_KEY))); } - this.partitionKey = partitionKey; if (rowKey == null || rowKey.isEmpty()) { throw logger.logExceptionAsError(new IllegalArgumentException(String.format( "'%s' is an empty value.", TablesConstants.ROW_KEY))); } - this.rowKey = rowKey; - - this.timestamp = null; - this.eTag = null; - this.odataEditLink = null; - this.odataId = null; - this.odataType = null; this.properties = new HashMap<>(); properties.put(TablesConstants.PARTITION_KEY, partitionKey); properties.put(TablesConstants.ROW_KEY, rowKey); } - TableEntity(Map properties) { - Object partitionKey = properties.get(TablesConstants.PARTITION_KEY); - if (partitionKey != null && (!(partitionKey instanceof String) || ((String) partitionKey).isEmpty())) { - throw logger.logExceptionAsError(new IllegalArgumentException(String.format( - "'%s' is an empty value or is of the wrong type.", TablesConstants.PARTITION_KEY))); - } - this.partitionKey = (String) partitionKey; - - Object rowKey = properties.get(TablesConstants.ROW_KEY); - if (rowKey != null && (!(rowKey instanceof String) || ((String) rowKey).isEmpty())) { - throw logger.logExceptionAsError(new IllegalArgumentException(String.format( - "'%s' is an empty value or is of the wrong type.", TablesConstants.ROW_KEY))); - } - this.rowKey = (String) rowKey; - - Object timestamp = properties.get(TablesConstants.TIMESTAMP_KEY); - if (timestamp != null && !(timestamp instanceof OffsetDateTime)) { - throw logger.logExceptionAsError(new IllegalArgumentException(String.format( - "'%s' value is of the wrong type.", TablesConstants.TIMESTAMP_KEY))); - } - this.timestamp = (OffsetDateTime) timestamp; - - Object eTag = properties.get(TablesConstants.ODATA_ETAG_KEY); - if (eTag != null && !(eTag instanceof String)) { - throw logger.logExceptionAsError(new IllegalArgumentException(String.format( - "'%s' value is of the wrong type.", TablesConstants.ODATA_ETAG_KEY))); - } - this.eTag = (String) eTag; - - Object odataEditLink = properties.get(TablesConstants.ODATA_EDIT_LINK_KEY); - if (odataEditLink != null && !(odataEditLink instanceof String)) { - throw logger.logExceptionAsError(new IllegalArgumentException(String.format( - "'%s' value is of the wrong type.", TablesConstants.ODATA_EDIT_LINK_KEY))); - } - this.odataEditLink = (String) odataEditLink; - - Object odataId = properties.get(TablesConstants.ODATA_ID_KEY); - if (odataId != null && !(odataId instanceof String)) { - throw logger.logExceptionAsError(new IllegalArgumentException(String.format( - "'%s' value is of the wrong type.", TablesConstants.ODATA_ID_KEY))); - } - this.odataId = (String) odataId; - - Object odataType = properties.get(TablesConstants.ODATA_TYPE_KEY); - if (odataType != null && !(odataType instanceof String)) { - throw logger.logExceptionAsError(new IllegalArgumentException(String.format( - "'%s' value is of the wrong type.", TablesConstants.ODATA_TYPE_KEY))); - } - this.odataType = (String) odataType; + private TableEntity() { + this.properties = new HashMap<>(); + } - this.properties = properties; + /** + * Gets a single property from the properties map + * + * @param key Key for the property. + * @return Value of the property. + */ + public final Object getProperty(String key) { + return properties.get(key); } /** @@ -122,32 +67,60 @@ public TableEntity(String partitionKey, String rowKey) { * * @return map of properties representing this entity */ - public Map getProperties() { + public final Map getProperties() { return properties; } /** * Adds a property to the entity. * - * @param key Key to for the property. + * @param key Key for the property. * @param value Value of the property. * * @return The updated {@link TableEntity} object. * @throws NullPointerException if {@code key} is null. */ - public TableEntity addProperty(String key, Object value) { + public final TableEntity addProperty(String key, Object value) { + validateProperty(key, value); + properties.put(key, value); + return this; + } + + /** + * Adds properties to the entity. + * + * @param properties The map of properties to add. + * + * @return The updated {@link TableEntity} object. + */ + public final TableEntity addProperties(Map properties) { + for (Map.Entry entry : properties.entrySet()) { + validateProperty(entry.getKey(), entry.getValue()); + } + this.properties.putAll(properties); + return this; + } + + private void validateProperty(String key, Object value) { Objects.requireNonNull(key, "'key' cannot be null."); - if (TablesConstants.PARTITION_KEY.equals(key)) { - throw logger.logExceptionAsError( - new IllegalArgumentException(TablesConstants.PARTITION_KEY + " cannot be set after object creation.")); - } else if (TablesConstants.ROW_KEY.equals(key)) { - throw logger.logExceptionAsError( - new IllegalArgumentException(TablesConstants.ROW_KEY + " cannot be set after object creation.")); + if ((TablesConstants.PARTITION_KEY.equals(key) || TablesConstants.ROW_KEY.equals(key)) && value != null + && (!(value instanceof String) || ((String) value).isEmpty())) { + throw logger.logExceptionAsError(new IllegalArgumentException(String.format( + "'%s' must be a non-empty String.", key))); } - properties.put(key, value); - return this; + if (TablesConstants.TIMESTAMP_KEY.equals(key) && value != null && !(value instanceof OffsetDateTime)) { + throw logger.logExceptionAsError(new IllegalArgumentException(String.format( + "'%s' must be an OffsetDateTime.", key))); + } + + if ((TablesConstants.ODATA_ETAG_KEY.equals(key) || TablesConstants.ODATA_EDIT_LINK_KEY.equals(key) + || TablesConstants.ODATA_ID_KEY.equals(key) || TablesConstants.ODATA_TYPE_KEY.equals(key)) && value != null + && !(value instanceof String)) { + throw logger.logExceptionAsError(new IllegalArgumentException(String.format( + "'%s' must be a String.", key))); + } } /** @@ -155,8 +128,8 @@ public TableEntity addProperty(String key, Object value) { * * @return the row key for the given entity */ - public String getRowKey() { - return rowKey; + public final String getRowKey() { + return (String) properties.get(TablesConstants.ROW_KEY); } /** @@ -164,8 +137,8 @@ public String getRowKey() { * * @return the partition key for the given entity */ - public String getPartitionKey() { - return partitionKey; + public final String getPartitionKey() { + return (String) properties.get(TablesConstants.PARTITION_KEY); } /** @@ -173,8 +146,8 @@ public String getPartitionKey() { * * @return the Timestamp for the entity */ - public OffsetDateTime getTimestamp() { - return timestamp; + public final OffsetDateTime getTimestamp() { + return (OffsetDateTime) properties.get(TablesConstants.TIMESTAMP_KEY); } /** @@ -182,8 +155,8 @@ public OffsetDateTime getTimestamp() { * * @return the etag for the entity */ - public String getETag() { - return eTag; + public final String getETag() { + return (String) properties.get(TablesConstants.ODATA_ETAG_KEY); } /** @@ -191,8 +164,8 @@ public String getETag() { * * @return type */ - String getOdataType() { - return odataType; + final String getOdataType() { + return (String) properties.get(TablesConstants.ODATA_TYPE_KEY); } /** @@ -200,8 +173,8 @@ String getOdataType() { * * @return ID */ - String getOdataId() { - return odataId; + final String getOdataId() { + return (String) properties.get(TablesConstants.ODATA_ID_KEY); } /** @@ -209,7 +182,7 @@ String getOdataId() { * * @return edit link */ - String getOdataEditLink() { - return odataEditLink; + final String getOdataEditLink() { + return (String) properties.get(TablesConstants.ODATA_EDIT_LINK_KEY); } } diff --git a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/EntityHelperTest.java b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/EntityHelperTest.java new file mode 100644 index 0000000000000..dfc793a5b3554 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/EntityHelperTest.java @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables; + +import com.azure.core.util.logging.ClientLogger; +import com.azure.data.tables.models.TableEntity; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class EntityHelperTest { + private final ClientLogger logger = new ClientLogger(EntityHelperTest.class); + + @Test + public void testConvertToSubclass() { + byte[] bytes = new byte[]{1, 2, 3}; + boolean b = true; + OffsetDateTime dateTime = OffsetDateTime.now(); + double d = 1.23D; + UUID uuid = UUID.randomUUID(); + int i = 123; + long l = 123L; + String s = "Test"; + SampleEntity.Color color = SampleEntity.Color.GREEN; + + Map props = new HashMap<>(); + props.put("ByteField", bytes); + props.put("BooleanField", b); + props.put("DateTimeField", dateTime); + props.put("DoubleField", d); + props.put("UuidField", uuid); + props.put("IntField", i); + props.put("LongField", l); + props.put("StringField", s); + props.put("EnumField", color); + + TableEntity entity = new TableEntity("abc", "def"); + entity.addProperties(props); + + SampleEntity result = EntityHelper.convertToSubclass(entity, SampleEntity.class, logger); + Assertions.assertEquals(bytes, result.getByteField()); + Assertions.assertEquals(b, result.getBooleanField()); + Assertions.assertEquals(dateTime, result.getDateTimeField()); + Assertions.assertEquals(d, result.getDoubleField()); + Assertions.assertEquals(uuid, result.getUuidField()); + Assertions.assertEquals(i, result.getIntField()); + Assertions.assertEquals(l, result.getLongField()); + Assertions.assertEquals(s, result.getStringField()); + Assertions.assertEquals(color, result.getEnumField()); + } + + @Test + public void testSetPropertiesFromGetters() { + byte[] bytes = new byte[]{1, 2, 3}; + boolean b = true; + OffsetDateTime dateTime = OffsetDateTime.now(); + double d = 1.23D; + UUID uuid = UUID.randomUUID(); + int i = 123; + long l = 123L; + String s = "Test"; + SampleEntity.Color color = SampleEntity.Color.GREEN; + + SampleEntity entity = new SampleEntity("abc", "def"); + entity.setByteField(bytes); + entity.setBooleanField(b); + entity.setDateTimeField(dateTime); + entity.setDoubleField(d); + entity.setUuidField(uuid); + entity.setIntField(i); + entity.setLongField(l); + entity.setStringField(s); + entity.setEnumField(color); + + EntityHelper.setPropertiesFromGetters(entity, logger); + + Assertions.assertEquals(entity.getProperties().get("ByteField"), bytes); + Assertions.assertEquals(entity.getProperties().get("BooleanField"), b); + Assertions.assertEquals(entity.getProperties().get("DateTimeField"), dateTime); + Assertions.assertEquals(entity.getProperties().get("DoubleField"), d); + Assertions.assertEquals(entity.getProperties().get("UuidField"), uuid); + Assertions.assertEquals(entity.getProperties().get("IntField"), i); + Assertions.assertEquals(entity.getProperties().get("LongField"), l); + Assertions.assertEquals(entity.getProperties().get("StringField"), s); + Assertions.assertEquals(entity.getProperties().get("EnumField"), color); + } +} diff --git a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/SampleEntity.java b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/SampleEntity.java new file mode 100644 index 0000000000000..799d0ba674735 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/SampleEntity.java @@ -0,0 +1,121 @@ +// 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 java.time.OffsetDateTime; +import java.util.UUID; + +public class SampleEntity extends TableEntity { + private byte[] byteField; + private boolean booleanField; + private OffsetDateTime dateTimeField; + private double doubleField; + private UUID uuidField; + private int intField; + private long longField; + private String stringField; + private Color enumField; + + public SampleEntity(String partitionKey, String rowKey) { + super(partitionKey, rowKey); + } + + public int getIntField() { + return intField; + } + + public void setIntField(int intField) { + this.intField = intField; + } + + public byte[] getByteField() { + return byteField; + } + + public void setByteField(byte[] byteField) { + this.byteField = byteField; + } + + public boolean isBooleanField() { + return booleanField; + } + + public boolean getBooleanField() { + return booleanField; + } + + public void setBooleanField(boolean booleanField) { + this.booleanField = booleanField; + } + + public OffsetDateTime getDateTimeField() { + return dateTimeField; + } + + public void setDateTimeField(OffsetDateTime dateTimeField) { + this.dateTimeField = dateTimeField; + } + + public double getDoubleField() { + return doubleField; + } + + public void setDoubleField(double doubleField) { + this.doubleField = doubleField; + } + + public UUID getUuidField() { + return uuidField; + } + + public void setUuidField(UUID uuidField) { + this.uuidField = uuidField; + } + + public long getLongField() { + return longField; + } + + public void setLongField(long longField) { + this.longField = longField; + } + + public String getStringField() { + return stringField; + } + + public void setStringField(String stringField) { + this.stringField = stringField; + } + + public Color getEnumField() { + return enumField; + } + + public void setEnumField(Color enumField) { + this.enumField = enumField; + } + + public enum Color { + RED(1), + GREEN(2), + BLUE(3); + + private final int value; + + Color(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + } +} diff --git a/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/SingleFieldEntity.java b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/SingleFieldEntity.java new file mode 100644 index 0000000000000..5aa838a6e4452 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/java/com/azure/data/tables/SingleFieldEntity.java @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.data.tables; + +import com.azure.data.tables.models.TableEntity; + +public class SingleFieldEntity extends TableEntity { + private String subclassProperty; + + public SingleFieldEntity(String partitionKey, String rowKey) { + super(partitionKey, rowKey); + } + + public String getSubclassProperty() { + return subclassProperty; + } + + public void setSubclassProperty(String subclassProperty) { + this.subclassProperty = subclassProperty; + } +} 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 8642e6d681402..a884833cb7f76 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 @@ -21,10 +21,13 @@ import java.time.Duration; import java.time.OffsetDateTime; +import java.time.ZoneOffset; import java.util.Date; +import java.util.HashMap; import java.util.Map; import java.util.UUID; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; @@ -216,6 +219,52 @@ void createEntityWithAllSupportedDataTypesAsync() { .verify(); } + @Test + void createEntitySubclassAsync() { + // Arrange + String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); + String rowKeyValue = testResourceNamer.randomName("rowKey", 20); + byte[] bytes = new byte[]{1, 2, 3}; + boolean b = true; + OffsetDateTime dateTime = OffsetDateTime.of(2020, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + double d = 1.23D; + UUID uuid = UUID.fromString("11111111-2222-3333-4444-555555555555"); + int i = 123; + long l = 123L; + String s = "Test"; + SampleEntity.Color color = SampleEntity.Color.GREEN; + + SampleEntity tableEntity = new SampleEntity(partitionKeyValue, rowKeyValue); + tableEntity.setByteField(bytes); + tableEntity.setBooleanField(b); + tableEntity.setDateTimeField(dateTime); + tableEntity.setDoubleField(d); + tableEntity.setUuidField(uuid); + tableEntity.setIntField(i); + tableEntity.setLongField(l); + tableEntity.setStringField(s); + tableEntity.setEnumField(color); + + tableClient.createEntity(tableEntity).block(TIMEOUT); + + // Act & Assert + StepVerifier.create(tableClient.getEntityWithResponse(partitionKeyValue, rowKeyValue, null)) + .assertNext(response -> { + TableEntity entity = response.getValue(); + assertArrayEquals((byte[]) entity.getProperties().get("ByteField"), bytes); + assertEquals(entity.getProperties().get("BooleanField"), b); + assertTrue(dateTime.isEqual((OffsetDateTime) entity.getProperties().get("DateTimeField"))); + assertEquals(entity.getProperties().get("DoubleField"), d); + assertEquals(0, uuid.compareTo((UUID) entity.getProperties().get("UuidField"))); + assertEquals(entity.getProperties().get("IntField"), i); + assertEquals(entity.getProperties().get("LongField"), l); + assertEquals(entity.getProperties().get("StringField"), s); + assertEquals(entity.getProperties().get("EnumField"), color.name()); + }) + .expectComplete() + .verify(); + } + @Test void deleteTableAsync() { // Act & Assert @@ -355,6 +404,65 @@ void getEntityWithResponseWithSelectAsync() { .verify(); } + @Test + void getEntityWithResponseSubclassAsync() { + // Arrange + String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); + String rowKeyValue = testResourceNamer.randomName("rowKey", 20); + byte[] bytes = new byte[]{1, 2, 3}; + boolean b = true; + OffsetDateTime dateTime = OffsetDateTime.of(2020, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC); + double d = 1.23D; + UUID uuid = UUID.fromString("11111111-2222-3333-4444-555555555555"); + int i = 123; + long l = 123L; + String s = "Test"; + SampleEntity.Color color = SampleEntity.Color.GREEN; + + Map props = new HashMap<>(); + props.put("ByteField", bytes); + props.put("BooleanField", b); + props.put("DateTimeField", dateTime); + props.put("DoubleField", d); + props.put("UuidField", uuid); + props.put("IntField", i); + props.put("LongField", l); + props.put("StringField", s); + props.put("EnumField", color); + + TableEntity tableEntity = new TableEntity(partitionKeyValue, rowKeyValue); + tableEntity.addProperties(props); + + int expectedStatusCode = 200; + tableClient.createEntity(tableEntity).block(TIMEOUT); + + // Act & Assert + StepVerifier.create(tableClient.getEntityWithResponse(partitionKeyValue, rowKeyValue, null, SampleEntity.class)) + .assertNext(response -> { + SampleEntity entity = response.getValue(); + assertEquals(expectedStatusCode, response.getStatusCode()); + + assertNotNull(entity); + assertEquals(tableEntity.getPartitionKey(), entity.getPartitionKey()); + assertEquals(tableEntity.getRowKey(), entity.getRowKey()); + + assertNotNull(entity.getTimestamp()); + assertNotNull(entity.getETag()); + + assertArrayEquals(bytes, entity.getByteField()); + assertEquals(b, entity.getBooleanField()); + assertTrue(dateTime.isEqual(entity.getDateTimeField())); + assertEquals(d, entity.getDoubleField()); + assertEquals(0, uuid.compareTo(entity.getUuidField())); + assertEquals(i, entity.getIntField()); + assertEquals(l, entity.getLongField()); + assertEquals(s, entity.getStringField()); + assertEquals(color, entity.getEnumField()); + }) + .expectComplete() + .verify(); + } + @Test void updateEntityWithResponseReplaceAsync() { updateEntityWithResponseAsync(UpdateMode.REPLACE); @@ -411,6 +519,33 @@ void updateEntityWithResponseAsync(UpdateMode mode) { } } + @Test + void updateEntityWithResponseSubclassAsync() { + // Arrange + String partitionKeyValue = testResourceNamer.randomName("APartitionKey", 20); + String rowKeyValue = testResourceNamer.randomName("ARowKey", 20); + int expectedStatusCode = 204; + + SingleFieldEntity tableEntity = new SingleFieldEntity(partitionKeyValue, rowKeyValue); + tableEntity.setSubclassProperty("InitialValue"); + tableClient.createEntity(tableEntity).block(TIMEOUT); + + // Act & Assert + tableEntity.setSubclassProperty("UpdatedValue"); + StepVerifier.create(tableClient.updateEntityWithResponse(tableEntity, true, UpdateMode.MERGE)) + .assertNext(response -> assertEquals(expectedStatusCode, response.getStatusCode())) + .expectComplete() + .verify(); + + StepVerifier.create(tableClient.getEntity(partitionKeyValue, rowKeyValue)) + .assertNext(entity -> { + Map properties = entity.getProperties(); + assertTrue(properties.containsKey("SubclassProperty")); + assertEquals("UpdatedValue", properties.get("SubclassProperty")); + }) + .verifyComplete(); + } + @Test @Tag("ListEntities") void listEntitiesAsync() { @@ -497,4 +632,22 @@ void listEntitiesWithTopAsync() { .expectComplete() .verify(); } + + @Test + @Tag("ListEntities") + void listEntitiesSubclassAsync() { + // Arrange + String partitionKeyValue = testResourceNamer.randomName("partitionKey", 20); + String rowKeyValue = testResourceNamer.randomName("rowKey", 20); + String rowKeyValue2 = testResourceNamer.randomName("rowKey", 20); + tableClient.createEntity(new TableEntity(partitionKeyValue, rowKeyValue)).block(TIMEOUT); + tableClient.createEntity(new TableEntity(partitionKeyValue, rowKeyValue2)).block(TIMEOUT); + + // Act & Assert + StepVerifier.create(tableClient.listEntities(SampleEntity.class)) + .expectNextCount(2) + .thenConsumeWhile(x -> true) + .expectComplete() + .verify(); + } } diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/createEntitySubclassAsync.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/createEntitySubclassAsync.json new file mode 100644 index 0000000000000..b3a75ce773022 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/resources/session-records/createEntitySubclassAsync.json @@ -0,0 +1,79 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/Tables", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "96d7ae74-e470-4838-a9d8-46a4871f3e68", + "Content-Type" : "application/json" + }, + "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, 30 Sep 2020 21:16:32 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://brsiegelsample.table.core.windows.net/Tables('tablename57887c8d')", + "Content-Length" : "0", + "x-ms-request-id" : "ff50b96b-0002-00c9-666e-9777b1000000", + "Preference-Applied" : "return-no-content", + "x-ms-client-request-id" : "96d7ae74-e470-4838-a9d8-46a4871f3e68", + "Location" : "https://brsiegelsample.table.core.windows.net/Tables('tablename57887c8d')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename57887c8d", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "255b8138-abbf-4da2-81e6-9ed38eb9dcee", + "Content-Type" : "application/json" + }, + "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, 30 Sep 2020 21:16:32 GMT", + "Cache-Control" : "no-cache", + "ETag" : "W/datetime'2020-09-30T21%3A16%3A33.5959747Z'", + "DataServiceId" : "https://brsiegelsample.table.core.windows.net/tablename57887c8d(PartitionKey='partitionkey984251',RowKey='rowkey14577b3f6')", + "Content-Length" : "0", + "x-ms-request-id" : "ff50b97b-0002-00c9-726e-9777b1000000", + "Preference-Applied" : "return-no-content", + "x-ms-client-request-id" : "255b8138-abbf-4da2-81e6-9ed38eb9dcee", + "Location" : "https://brsiegelsample.table.core.windows.net/tablename57887c8d(PartitionKey='partitionkey984251',RowKey='rowkey14577b3f6')" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.table.core.windows.net/tablename57887c8d(PartitionKey='partitionkey984251',RowKey='rowkey14577b3f6')?$format=application/json%3Bodata%3Dfullmetadata", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "66565ca0-f3d5-4b2f-bf7e-2632151cc45a" + }, + "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, 30 Sep 2020 21:16:32 GMT", + "Cache-Control" : "no-cache", + "ETag" : "W/datetime'2020-09-30T21%3A16%3A33.5959747Z'", + "x-ms-request-id" : "ff50b982-0002-00c9-796e-9777b1000000", + "Body" : "{\"odata.metadata\":\"https://brsiegelsample.table.core.windows.net/$metadata#tablename57887c8d/@Element\",\"odata.type\":\"brsiegelsample.tablename57887c8d\",\"odata.id\":\"https://brsiegelsample.table.core.windows.net/tablename57887c8d(PartitionKey='partitionkey984251',RowKey='rowkey14577b3f6')\",\"odata.etag\":\"W/\\\"datetime'2020-09-30T21%3A16%3A33.5959747Z'\\\"\",\"odata.editLink\":\"tablename57887c8d(PartitionKey='partitionkey984251',RowKey='rowkey14577b3f6')\",\"PartitionKey\":\"partitionkey984251\",\"RowKey\":\"rowkey14577b3f6\",\"Timestamp@odata.type\":\"Edm.DateTime\",\"Timestamp\":\"2020-09-30T21:16:33.5959747Z\",\"EnumField\":\"GREEN\",\"IntField\":123,\"DateTimeField@odata.type\":\"Edm.DateTime\",\"DateTimeField\":\"2020-01-01T00:00:00Z\",\"LongField@odata.type\":\"Edm.Int64\",\"LongField\":\"123\",\"BooleanField\":true,\"StringField\":\"Test\",\"ByteField@odata.type\":\"Edm.Binary\",\"ByteField\":\"AQID\",\"DoubleField\":1.23,\"UuidField@odata.type\":\"Edm.Guid\",\"UuidField\":\"11111111-2222-3333-4444-555555555555\"}", + "x-ms-client-request-id" : "66565ca0-f3d5-4b2f-bf7e-2632151cc45a", + "Content-Type" : "application/json;odata=fullmetadata;streaming=true;charset=utf-8" + }, + "Exception" : null + } ], + "variables" : [ "tablename57887c8d", "partitionkey984251", "rowkey14577b3f6" ] +} \ No newline at end of file diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/getEntityWithResponseSubclassAsync.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/getEntityWithResponseSubclassAsync.json new file mode 100644 index 0000000000000..b72a2a1b53d5a --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/resources/session-records/getEntityWithResponseSubclassAsync.json @@ -0,0 +1,79 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/Tables", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "59aa16e4-a5c5-474a-b3b8-3666a38d1e47", + "Content-Type" : "application/json" + }, + "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, 30 Sep 2020 21:16:19 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://brsiegelsample.table.core.windows.net/Tables('tablename45789051')", + "Content-Length" : "0", + "x-ms-request-id" : "7a5e4893-9002-00cb-1d6e-97c909000000", + "Preference-Applied" : "return-no-content", + "x-ms-client-request-id" : "59aa16e4-a5c5-474a-b3b8-3666a38d1e47", + "Location" : "https://brsiegelsample.table.core.windows.net/Tables('tablename45789051')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename45789051", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "67707a0c-224d-41e5-8350-3f2ddffe911e", + "Content-Type" : "application/json" + }, + "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, 30 Sep 2020 21:16:19 GMT", + "Cache-Control" : "no-cache", + "ETag" : "W/datetime'2020-09-30T21%3A16%3A20.5101455Z'", + "DataServiceId" : "https://brsiegelsample.table.core.windows.net/tablename45789051(PartitionKey='partitionkey29962b',RowKey='rowkey95857fef3')", + "Content-Length" : "0", + "x-ms-request-id" : "7a5e48a2-9002-00cb-296e-97c909000000", + "Preference-Applied" : "return-no-content", + "x-ms-client-request-id" : "67707a0c-224d-41e5-8350-3f2ddffe911e", + "Location" : "https://brsiegelsample.table.core.windows.net/tablename45789051(PartitionKey='partitionkey29962b',RowKey='rowkey95857fef3')" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.table.core.windows.net/tablename45789051(PartitionKey='partitionkey29962b',RowKey='rowkey95857fef3')?$format=application/json%3Bodata%3Dfullmetadata", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "d0be84c3-0f91-4f7e-b037-79e520b9a141" + }, + "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, 30 Sep 2020 21:16:19 GMT", + "Cache-Control" : "no-cache", + "ETag" : "W/datetime'2020-09-30T21%3A16%3A20.5101455Z'", + "x-ms-request-id" : "7a5e48a7-9002-00cb-2e6e-97c909000000", + "Body" : "{\"odata.metadata\":\"https://brsiegelsample.table.core.windows.net/$metadata#tablename45789051/@Element\",\"odata.type\":\"brsiegelsample.tablename45789051\",\"odata.id\":\"https://brsiegelsample.table.core.windows.net/tablename45789051(PartitionKey='partitionkey29962b',RowKey='rowkey95857fef3')\",\"odata.etag\":\"W/\\\"datetime'2020-09-30T21%3A16%3A20.5101455Z'\\\"\",\"odata.editLink\":\"tablename45789051(PartitionKey='partitionkey29962b',RowKey='rowkey95857fef3')\",\"PartitionKey\":\"partitionkey29962b\",\"RowKey\":\"rowkey95857fef3\",\"Timestamp@odata.type\":\"Edm.DateTime\",\"Timestamp\":\"2020-09-30T21:16:20.5101455Z\",\"EnumField\":\"GREEN\",\"IntField\":123,\"DateTimeField@odata.type\":\"Edm.DateTime\",\"DateTimeField\":\"2020-01-01T00:00:00Z\",\"LongField@odata.type\":\"Edm.Int64\",\"LongField\":\"123\",\"BooleanField\":true,\"StringField\":\"Test\",\"ByteField@odata.type\":\"Edm.Binary\",\"ByteField\":\"AQID\",\"DoubleField\":1.23,\"UuidField@odata.type\":\"Edm.Guid\",\"UuidField\":\"11111111-2222-3333-4444-555555555555\"}", + "x-ms-client-request-id" : "d0be84c3-0f91-4f7e-b037-79e520b9a141", + "Content-Type" : "application/json;odata=fullmetadata;streaming=true;charset=utf-8" + }, + "Exception" : null + } ], + "variables" : [ "tablename45789051", "partitionkey29962b", "rowkey95857fef3" ] +} \ No newline at end of file diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/listEntitiesSubclassAsync.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/listEntitiesSubclassAsync.json new file mode 100644 index 0000000000000..9f20c4d71235f --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/resources/session-records/listEntitiesSubclassAsync.json @@ -0,0 +1,104 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/Tables", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "30fd1df1-78c7-40a1-80dd-270de290883d", + "Content-Type" : "application/json" + }, + "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, 30 Sep 2020 00:58:34 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://brsiegelsample.table.core.windows.net/Tables('tablename02141f53')", + "Content-Length" : "0", + "x-ms-request-id" : "104b351e-6002-003b-2ec4-968ff8000000", + "Preference-Applied" : "return-no-content", + "x-ms-client-request-id" : "30fd1df1-78c7-40a1-80dd-270de290883d", + "Location" : "https://brsiegelsample.table.core.windows.net/Tables('tablename02141f53')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename02141f53", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "a8e41a7a-dc91-473b-87e3-44f7cadfbadb", + "Content-Type" : "application/json" + }, + "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, 30 Sep 2020 00:58:34 GMT", + "Cache-Control" : "no-cache", + "ETag" : "W/datetime'2020-09-30T00%3A58%3A35.2160344Z'", + "DataServiceId" : "https://brsiegelsample.table.core.windows.net/tablename02141f53(PartitionKey='partitionkey17046a',RowKey='rowkey882986696')", + "Content-Length" : "0", + "x-ms-request-id" : "104b3533-6002-003b-3fc4-968ff8000000", + "Preference-Applied" : "return-no-content", + "x-ms-client-request-id" : "a8e41a7a-dc91-473b-87e3-44f7cadfbadb", + "Location" : "https://brsiegelsample.table.core.windows.net/tablename02141f53(PartitionKey='partitionkey17046a',RowKey='rowkey882986696')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename02141f53", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "18a46345-9023-4a28-b5bc-29ef24a71a4d", + "Content-Type" : "application/json" + }, + "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, 30 Sep 2020 00:58:34 GMT", + "Cache-Control" : "no-cache", + "ETag" : "W/datetime'2020-09-30T00%3A58%3A35.4021664Z'", + "DataServiceId" : "https://brsiegelsample.table.core.windows.net/tablename02141f53(PartitionKey='partitionkey17046a',RowKey='rowkey29055bd41')", + "Content-Length" : "0", + "x-ms-request-id" : "104b3554-6002-003b-5ec4-968ff8000000", + "Preference-Applied" : "return-no-content", + "x-ms-client-request-id" : "18a46345-9023-4a28-b5bc-29ef24a71a4d", + "Location" : "https://brsiegelsample.table.core.windows.net/tablename02141f53(PartitionKey='partitionkey17046a',RowKey='rowkey29055bd41')" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.table.core.windows.net/tablename02141f53()?$format=application/json%3Bodata%3Dfullmetadata", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "ba0ddbcb-eebf-472b-9a04-532971a77b4d" + }, + "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" : "200", + "x-ms-request-id" : "104b356a-6002-003b-74c4-968ff8000000", + "Body" : "{\"odata.metadata\":\"https://brsiegelsample.table.core.windows.net/$metadata#tablename02141f53\",\"value\":[{\"odata.type\":\"brsiegelsample.tablename02141f53\",\"odata.id\":\"https://brsiegelsample.table.core.windows.net/tablename02141f53(PartitionKey='partitionkey17046a',RowKey='rowkey29055bd41')\",\"odata.etag\":\"W/\\\"datetime'2020-09-30T00%3A58%3A35.4021664Z'\\\"\",\"odata.editLink\":\"tablename02141f53(PartitionKey='partitionkey17046a',RowKey='rowkey29055bd41')\",\"PartitionKey\":\"partitionkey17046a\",\"RowKey\":\"rowkey29055bd41\",\"Timestamp@odata.type\":\"Edm.DateTime\",\"Timestamp\":\"2020-09-30T00:58:35.4021664Z\"},{\"odata.type\":\"brsiegelsample.tablename02141f53\",\"odata.id\":\"https://brsiegelsample.table.core.windows.net/tablename02141f53(PartitionKey='partitionkey17046a',RowKey='rowkey882986696')\",\"odata.etag\":\"W/\\\"datetime'2020-09-30T00%3A58%3A35.2160344Z'\\\"\",\"odata.editLink\":\"tablename02141f53(PartitionKey='partitionkey17046a',RowKey='rowkey882986696')\",\"PartitionKey\":\"partitionkey17046a\",\"RowKey\":\"rowkey882986696\",\"Timestamp@odata.type\":\"Edm.DateTime\",\"Timestamp\":\"2020-09-30T00:58:35.2160344Z\"}]}", + "Date" : "Wed, 30 Sep 2020 00:58:34 GMT", + "x-ms-client-request-id" : "ba0ddbcb-eebf-472b-9a04-532971a77b4d", + "Content-Type" : "application/json;odata=fullmetadata;streaming=true;charset=utf-8" + }, + "Exception" : null + } ], + "variables" : [ "tablename02141f53", "partitionkey17046a", "rowkey882986696", "rowkey29055bd41" ] +} \ No newline at end of file diff --git a/sdk/tables/azure-data-tables/src/test/resources/session-records/updateEntityWithResponseSubclassAsync.json b/sdk/tables/azure-data-tables/src/test/resources/session-records/updateEntityWithResponseSubclassAsync.json new file mode 100644 index 0000000000000..d19477e9febe9 --- /dev/null +++ b/sdk/tables/azure-data-tables/src/test/resources/session-records/updateEntityWithResponseSubclassAsync.json @@ -0,0 +1,102 @@ +{ + "networkCallRecords" : [ { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/Tables", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "2f244f3d-1e59-4190-9f0b-44cd6161756c", + "Content-Type" : "application/json" + }, + "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, 30 Sep 2020 00:58:05 GMT", + "Cache-Control" : "no-cache", + "DataServiceId" : "https://brsiegelsample.table.core.windows.net/Tables('tablename54368bea')", + "Content-Length" : "0", + "x-ms-request-id" : "13411e6a-1002-006c-55c4-9621cb000000", + "Preference-Applied" : "return-no-content", + "x-ms-client-request-id" : "2f244f3d-1e59-4190-9f0b-44cd6161756c", + "Location" : "https://brsiegelsample.table.core.windows.net/Tables('tablename54368bea')" + }, + "Exception" : null + }, { + "Method" : "POST", + "Uri" : "https://REDACTED.table.core.windows.net/tablename54368bea", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "916f87fb-6338-4ec0-9c99-364f3e060f11", + "Content-Type" : "application/json" + }, + "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, 30 Sep 2020 00:58:05 GMT", + "Cache-Control" : "no-cache", + "ETag" : "W/datetime'2020-09-30T00%3A58%3A06.5695489Z'", + "DataServiceId" : "https://brsiegelsample.table.core.windows.net/tablename54368bea(PartitionKey='apartitionkey197836',RowKey='arowkey2442208b8')", + "Content-Length" : "0", + "x-ms-request-id" : "13411e77-1002-006c-60c4-9621cb000000", + "Preference-Applied" : "return-no-content", + "x-ms-client-request-id" : "916f87fb-6338-4ec0-9c99-364f3e060f11", + "Location" : "https://brsiegelsample.table.core.windows.net/tablename54368bea(PartitionKey='apartitionkey197836',RowKey='arowkey2442208b8')" + }, + "Exception" : null + }, { + "Method" : "PATCH", + "Uri" : "https://REDACTED.table.core.windows.net/tablename54368bea(PartitionKey='apartitionkey197836',RowKey='arowkey2442208b8')", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "34ee8f9a-d9bc-4d02-94a0-a20e32b0df1d", + "Content-Type" : "application/json" + }, + "Response" : { + "x-ms-version" : "2019-02-02", + "Server" : "Windows-Azure-Table/1.0 Microsoft-HTTPAPI/2.0", + "Cache-Control" : "no-cache", + "ETag" : "W/datetime'2020-09-30T00%3A58%3A06.6087467Z'", + "X-Content-Type-Options" : "nosniff", + "retry-after" : "0", + "Content-Length" : "0", + "StatusCode" : "204", + "x-ms-request-id" : "13411e83-1002-006c-69c4-9621cb000000", + "Date" : "Wed, 30 Sep 2020 00:58:05 GMT", + "x-ms-client-request-id" : "34ee8f9a-d9bc-4d02-94a0-a20e32b0df1d" + }, + "Exception" : null + }, { + "Method" : "GET", + "Uri" : "https://REDACTED.table.core.windows.net/tablename54368bea(PartitionKey='apartitionkey197836',RowKey='arowkey2442208b8')?$format=application/json%3Bodata%3Dfullmetadata", + "Headers" : { + "x-ms-version" : "2019-02-02", + "User-Agent" : "azsdk-java-UnknownName/UnknownVersion (11.0.8; Mac OS X; 10.15.6)", + "x-ms-client-request-id" : "f004daa9-3b8a-4eb6-bec8-25ecf55b47ad" + }, + "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, 30 Sep 2020 00:58:05 GMT", + "Cache-Control" : "no-cache", + "ETag" : "W/datetime'2020-09-30T00%3A58%3A06.6087467Z'", + "x-ms-request-id" : "13411e8b-1002-006c-71c4-9621cb000000", + "Body" : "{\"odata.metadata\":\"https://brsiegelsample.table.core.windows.net/$metadata#tablename54368bea/@Element\",\"odata.type\":\"brsiegelsample.tablename54368bea\",\"odata.id\":\"https://brsiegelsample.table.core.windows.net/tablename54368bea(PartitionKey='apartitionkey197836',RowKey='arowkey2442208b8')\",\"odata.etag\":\"W/\\\"datetime'2020-09-30T00%3A58%3A06.6087467Z'\\\"\",\"odata.editLink\":\"tablename54368bea(PartitionKey='apartitionkey197836',RowKey='arowkey2442208b8')\",\"PartitionKey\":\"apartitionkey197836\",\"RowKey\":\"arowkey2442208b8\",\"Timestamp@odata.type\":\"Edm.DateTime\",\"Timestamp\":\"2020-09-30T00:58:06.6087467Z\",\"SubclassProperty\":\"UpdatedValue\"}", + "x-ms-client-request-id" : "f004daa9-3b8a-4eb6-bec8-25ecf55b47ad", + "Content-Type" : "application/json;odata=fullmetadata;streaming=true;charset=utf-8" + }, + "Exception" : null + } ], + "variables" : [ "tablename54368bea", "apartitionkey197836", "arowkey2442208b8" ] +} \ No newline at end of file