diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java index 8ecbe528ec05..c42b5c78bd42 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/CosmosClientBuilder.java @@ -4,6 +4,7 @@ import com.azure.data.cosmos.internal.Configs; import com.azure.data.cosmos.internal.Permission; +import com.azure.data.cosmos.sync.CosmosSyncClient; import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -36,7 +37,7 @@ public class CosmosClientBuilder { private TokenResolver tokenResolver; private CosmosKeyCredential cosmosKeyCredential; - CosmosClientBuilder() { + public CosmosClientBuilder() { } /** @@ -198,6 +199,11 @@ public CosmosClientBuilder cosmosKeyCredential(CosmosKeyCredential cosmosKeyCred */ public CosmosClient build() { + validateConfig(); + return new CosmosClient(this); + } + + private void validateConfig() { ifThrowIllegalArgException(this.serviceEndpoint == null, "cannot build client without service endpoint"); ifThrowIllegalArgException( this.keyOrResourceToken == null && (permissions == null || permissions.isEmpty()) @@ -205,8 +211,16 @@ public CosmosClient build() { "cannot build client without any one of key, resource token, permissions, token resolver, and cosmos key credential"); ifThrowIllegalArgException(cosmosKeyCredential != null && StringUtils.isEmpty(cosmosKeyCredential.key()), "cannot build client without key credential"); + } - return new CosmosClient(this); + /** + * Builds a cosmos sync client object with the provided properties + * @return CosmosSyncClient + */ + public CosmosSyncClient buildSyncClient() { + + validateConfig(); + return new CosmosSyncClient(this); } Configs configs() { diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncClient.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncClient.java new file mode 100644 index 000000000000..57dbbb762a75 --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncClient.java @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.CosmosClient; +import com.azure.data.cosmos.CosmosClientBuilder; +import com.azure.data.cosmos.CosmosClientException; +import com.azure.data.cosmos.CosmosDatabaseProperties; +import com.azure.data.cosmos.CosmosDatabaseRequestOptions; +import com.azure.data.cosmos.CosmosDatabaseResponse; +import com.azure.data.cosmos.FeedOptions; +import com.azure.data.cosmos.FeedResponse; +import com.azure.data.cosmos.SqlQuerySpec; +import reactor.core.Exceptions; +import reactor.core.publisher.Mono; + +import java.util.Iterator; + +/** + * Provides a client-side logical representation of the Azure Cosmos database service. + * SyncClient is used to perform operations in a synchronous way + */ +public class CosmosSyncClient implements AutoCloseable { + private final CosmosClient asyncClientWrapper; + + public CosmosSyncClient(CosmosClientBuilder builder) { + this.asyncClientWrapper = builder.build(); + } + + /** + * Instantiate the cosmos client builder to build cosmos client + * @return {@link CosmosClientBuilder} + */ + public static CosmosClientBuilder builder(){ + return new CosmosClientBuilder(); + } + + /** + * Create a Database if it does not already exist on the service + * + * @param databaseProperties {@link CosmosDatabaseProperties} the database properties + * @return the {@link CosmosSyncDatabaseResponse} with the created database. + * @throws CosmosClientException the cosmos client exception. + */ + public CosmosSyncDatabaseResponse createDatabaseIfNotExists(CosmosDatabaseProperties databaseProperties) + throws CosmosClientException { + return mapDatabaseResponseAndBlock(asyncClientWrapper.createDatabaseIfNotExists(databaseProperties)); + } + + /** + * Create a Database if it does not already exist on the service + * + * @param id the id of the database + * @return the {@link CosmosSyncDatabaseResponse} with the created database. + * @throws CosmosClientException the cosmos client exception. + */ + public CosmosSyncDatabaseResponse createDatabaseIfNotExists(String id) throws CosmosClientException { + return mapDatabaseResponseAndBlock(asyncClientWrapper.createDatabaseIfNotExists(id)); + } + + + /** + * Creates a database. + * + * @param databaseProperties {@link CosmosDatabaseProperties} the database properties. + * @param options the request options. + * @return the {@link CosmosSyncDatabaseResponse} with the created database. + * @throws CosmosClientException the cosmos client exception. + */ + public CosmosSyncDatabaseResponse createDatabase(CosmosDatabaseProperties databaseProperties, + CosmosDatabaseRequestOptions options) throws CosmosClientException { + return mapDatabaseResponseAndBlock(asyncClientWrapper.createDatabase(databaseProperties, options)); + } + + /** + * Creates a database. + * + * @param databaseProperties {@link CosmosDatabaseProperties} the database properties. + * @return the {@link CosmosSyncDatabaseResponse} with the created database. + * @throws CosmosClientException the cosmos client exception. + */ + public CosmosSyncDatabaseResponse createDatabase(CosmosDatabaseProperties databaseProperties) throws CosmosClientException { + return mapDatabaseResponseAndBlock(asyncClientWrapper.createDatabase(databaseProperties)); + } + + /** + * Creates a database. + * + * @param id the id of the database + * @return the {@link CosmosSyncDatabaseResponse} with the created database. + * @throws CosmosClientException the cosmos client exception. + */ + public CosmosSyncDatabaseResponse createDatabase(String id) throws CosmosClientException { + return mapDatabaseResponseAndBlock(asyncClientWrapper.createDatabase(id)); + + } + + /** + * Creates a database. + * + * @param databaseProperties {@link CosmosDatabaseProperties} the database properties. + * @param throughput the throughput + * @param options {@link CosmosDatabaseRequestOptions} the request options + * @return the {@link CosmosSyncDatabaseResponse} with the created database. + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncDatabaseResponse createDatabase(CosmosDatabaseProperties databaseProperties, + int throughput, + CosmosDatabaseRequestOptions options) throws CosmosClientException { + return mapDatabaseResponseAndBlock(asyncClientWrapper.createDatabase(databaseProperties, throughput, options)); + } + + /** + * Creates a database. + * + * @param databaseProperties {@link CosmosDatabaseProperties} the database properties. + * @param throughput the throughput + * @return the {@link CosmosSyncDatabaseResponse} with the created database. + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncDatabaseResponse createDatabase(CosmosDatabaseProperties databaseProperties, + int throughput) throws CosmosClientException { + return mapDatabaseResponseAndBlock(asyncClientWrapper.createDatabase(databaseProperties, throughput)); + } + + + /** + * Creates a database. + * + * @param id the id of the database + * @param throughput the throughput + * @return the {@link CosmosSyncDatabaseResponse} with the created database. + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncDatabaseResponse createDatabase(String id, int throughput) throws CosmosClientException { + return mapDatabaseResponseAndBlock(asyncClientWrapper.createDatabase(id, throughput)); + } + + CosmosSyncDatabaseResponse mapDatabaseResponseAndBlock(Mono databaseMono) + throws CosmosClientException { + try { + return databaseMono + .map(this::convertResponse) + .block(); + } catch (Exception ex) { + final Throwable throwable = Exceptions.unwrap(ex); + if (throwable instanceof CosmosClientException) { + throw (CosmosClientException) throwable; + } else { + throw ex; + } + } + } + + /** + * Reads all databases. + * + * @param options {@link FeedOptions}the feed options. + * @return the iterator for feed response with the read databases. + */ + public Iterator> readAllDatabases(FeedOptions options) { + return asyncClientWrapper.readAllDatabases(options) + .toIterable() + .iterator(); + } + + /** + * Reads all databases. + * + * @return the iterator for feed response with the read databases. + */ + public Iterator> readAllDatabases() { + return asyncClientWrapper.readAllDatabases() + .toIterable() + .iterator(); + } + + /** + * Query a database + * + * @param query the query + * @param options {@link FeedOptions}the feed options. + * @return the iterator for feed response with the obtained databases. + */ + public Iterator> queryDatabases(String query, FeedOptions options) { + return asyncClientWrapper.queryDatabases(query, options) + .toIterable() + .iterator(); + } + + /** + * Query a database + * + * @param querySpec {@link SqlQuerySpec} the query spec + * @param options the query + * @return the iterator for feed response with the obtained databases. + */ + public Iterator> queryDatabases(SqlQuerySpec querySpec, FeedOptions options) { + return asyncClientWrapper.queryDatabases(querySpec, options) + .toIterable() + .iterator(); + } + + /** + * Gets the database client + * + * @param id the id of the database + * @return {@link CosmosSyncDatabase} the cosmos sync database + */ + public CosmosSyncDatabase getDatabase(String id) { + return new CosmosSyncDatabase(id, this, asyncClientWrapper.getDatabase(id)); + } + + CosmosSyncDatabaseResponse convertResponse(CosmosDatabaseResponse response) { + return new CosmosSyncDatabaseResponse(response, this); + } + + CosmosClient asyncClient() { + return this.asyncClientWrapper; + } + + /** + * Close this {@link CosmosSyncClient} instance + */ + public void close() { + asyncClientWrapper.close(); + } + +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncContainer.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncContainer.java new file mode 100644 index 000000000000..6d34013a9d73 --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncContainer.java @@ -0,0 +1,263 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.ChangeFeedOptions; +import com.azure.data.cosmos.CosmosClientException; +import com.azure.data.cosmos.CosmosContainer; +import com.azure.data.cosmos.CosmosContainerProperties; +import com.azure.data.cosmos.CosmosContainerRequestOptions; +import com.azure.data.cosmos.CosmosItemProperties; +import com.azure.data.cosmos.CosmosItemRequestOptions; +import com.azure.data.cosmos.CosmosItemResponse; +import com.azure.data.cosmos.FeedOptions; +import com.azure.data.cosmos.FeedResponse; +import com.azure.data.cosmos.SqlQuerySpec; +import reactor.core.Exceptions; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Iterator; + +/** + * Provides synchronous methods for reading, deleting, and replacing existing Containers + * Provides methods for interacting with child resources (Items, Scripts, Conflicts) + */ +public class CosmosSyncContainer { + + private final CosmosContainer containerWrapper; + private final CosmosSyncDatabase database; + private final String id; + + /** + * Instantiates a new Cosmos sync container. + * + * @param id the id + * @param database the database + * @param container the container + */ + CosmosSyncContainer(String id, CosmosSyncDatabase database, CosmosContainer container) { + this.id = id; + this.database = database; + this.containerWrapper = container; + } + + /** + * Id string. + * + * @return the string + */ + public String id() { + return id; + } + + /** + * Read cosmos sync container response. + * + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse read() throws CosmosClientException { + return database.mapContainerResponseAndBlock(this.containerWrapper.read()); + } + + /** + * Read cosmos sync container response. + * + * @param options the options + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse read(CosmosContainerRequestOptions options) throws CosmosClientException { + return database.mapContainerResponseAndBlock(this.containerWrapper.read(options)); + } + + /** + * Delete cosmos sync container response. + * + * @param options the options + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse delete(CosmosContainerRequestOptions options) throws CosmosClientException { + return database.mapContainerResponseAndBlock(this.containerWrapper.delete(options)); + } + + /** + * Delete cosmos sync container response. + * + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse delete() throws CosmosClientException { + return database.mapContainerResponseAndBlock(this.containerWrapper.delete()); + } + + /** + * Replace cosmos sync container response. + * + * @param containerProperties the container properties + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse replace(CosmosContainerProperties containerProperties) throws CosmosClientException { + return database.mapContainerResponseAndBlock(this.containerWrapper.replace(containerProperties)); + } + + /** + * Replace cosmos sync container response. + * + * @param containerProperties the container properties + * @param options the options + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse replace(CosmosContainerProperties containerProperties, + CosmosContainerRequestOptions options) throws CosmosClientException { + return database.mapContainerResponseAndBlock(this.containerWrapper.replace(containerProperties, options)); + } + + + /* CosmosItem operations */ + + /** + * Create item cosmos sync item response. + * + * @param item the item + * @return the cosmos sync item response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncItemResponse createItem(Object item) throws CosmosClientException { + return this.mapItemResponseAndBlock(this.containerWrapper.createItem(item)); + } + + /** + * Create item cosmos sync item response. + * + * @param item the item + * @param options the options + * @return the cosmos sync item response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncItemResponse createItem(Object item, CosmosItemRequestOptions options) throws CosmosClientException { + return this.mapItemResponseAndBlock(this.containerWrapper.createItem(item, options)); + } + + /** + * Upsert item cosmos sync item response. + * + * @param item the item + * @return the cosmos sync item response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncItemResponse upsertItem(Object item) throws CosmosClientException { + return this.mapItemResponseAndBlock(this.containerWrapper.upsertItem(item)); + } + + /** + * Upsert item cosmos sync item response. + * + * @param item the item + * @param options the options + * @return the cosmos sync item response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncItemResponse upsertItem(Object item, CosmosItemRequestOptions options) throws CosmosClientException { + return this.mapItemResponseAndBlock(this.containerWrapper.createItem(item, options)); + } + + /** + * Map item response and block cosmos sync item response. + * + * @param itemMono the item mono + * @return the cosmos sync item response + * @throws CosmosClientException the cosmos client exception + */ + CosmosSyncItemResponse mapItemResponseAndBlock(Mono itemMono) + throws CosmosClientException { + try { + return itemMono + .map(this::convertResponse) + .block(); + } catch (Exception ex) { + final Throwable throwable = Exceptions.unwrap(ex); + if (throwable instanceof CosmosClientException) { + throw (CosmosClientException) throwable; + } else { + throw ex; + } + } + } + + /** + * Read all items iterator. + * + * @param options the options + * @return the iterator + */ + public Iterator> readAllItems(FeedOptions options) { + return getFeedIterator(this.containerWrapper.readAllItems(options)); + } + + /** + * Query items iterator. + * + * @param query the query + * @param options the options + * @return the iterator + */ + public Iterator> queryItems(String query, FeedOptions options) { + return getFeedIterator(this.containerWrapper.queryItems(query, options)); + } + + /** + * Query items iterator. + * + * @param querySpec the query spec + * @param options the options + * @return the iterator + */ + public Iterator> queryItems(SqlQuerySpec querySpec, FeedOptions options) { + return getFeedIterator(this.containerWrapper.queryItems(querySpec, options)); + } + + /** + * Query change feed items iterator. + * + * @param changeFeedOptions the change feed options + * @return the iterator + */ + public Iterator> queryChangeFeedItems(ChangeFeedOptions changeFeedOptions) { + return getFeedIterator(this.containerWrapper.queryChangeFeedItems(changeFeedOptions)); + } + + /** + * Gets item. + * + * @param id the id + * @param partitionKey the partition key + * @return the item + */ + public CosmosSyncItem getItem(String id, Object partitionKey) { + return new CosmosSyncItem(id, + partitionKey, + this, + containerWrapper.getItem(id, partitionKey)); + } + + /** + * Convert response cosmos sync item response. + * + * @param response the cosmos item response + * @return the cosmos sync item response + */ + CosmosSyncItemResponse convertResponse(CosmosItemResponse response) { + return new CosmosSyncItemResponse(response, null, this); + } + + private Iterator> getFeedIterator(Flux> itemFlux) { + return itemFlux.toIterable(1).iterator(); + } + +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncContainerResponse.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncContainerResponse.java new file mode 100644 index 000000000000..874ae58ce85f --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncContainerResponse.java @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.CosmosContainer; +import com.azure.data.cosmos.CosmosContainerProperties; +import com.azure.data.cosmos.CosmosContainerResponse; +import com.azure.data.cosmos.CosmosDatabase; +import com.azure.data.cosmos.CosmosResponse; +import com.azure.data.cosmos.internal.DocumentCollection; +import com.azure.data.cosmos.internal.ResourceResponse; + +/** + * The synchronous cosmos container response + */ +public class CosmosSyncContainerResponse { + + private final CosmosContainerResponse responseWrapper; + private final CosmosSyncContainer container; + + CosmosSyncContainerResponse(CosmosContainerResponse response, CosmosSyncDatabase database, CosmosSyncClient client) { + this.responseWrapper = response; + if (responseWrapper.container() != null) { + this.container = new CosmosSyncContainer(responseWrapper.container().id(), database, responseWrapper.container()); + } else { + // Delete will have null container client in response + this.container = null; + } + } + + /** + * Gets the progress of an index transformation, if one is underway. + * + * @return the progress of an index transformation. + */ + public long indexTransformationProgress() { + return responseWrapper.indexTransformationProgress(); + } + + /** + * Gets the container properties + * + * @return the cosmos container properties + */ + public CosmosContainerProperties properties() { + return responseWrapper.properties(); + } + + /** + * Gets the Container object + * + * @return the Cosmos container object + */ + public CosmosSyncContainer container() { + return container; + } +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncDatabase.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncDatabase.java new file mode 100644 index 000000000000..5875af50e8c0 --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncDatabase.java @@ -0,0 +1,346 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.CosmosClientException; +import com.azure.data.cosmos.CosmosContainerProperties; +import com.azure.data.cosmos.CosmosContainerRequestOptions; +import com.azure.data.cosmos.CosmosContainerResponse; +import com.azure.data.cosmos.CosmosDatabase; +import com.azure.data.cosmos.CosmosDatabaseRequestOptions; +import com.azure.data.cosmos.FeedOptions; +import com.azure.data.cosmos.FeedResponse; +import com.azure.data.cosmos.SqlQuerySpec; +import reactor.core.Exceptions; +import reactor.core.publisher.Mono; + +import java.util.Iterator; + +/** + * Perform read and delete databases, update database throughput, and perform operations on child resources in + * a synchronous way + */ +public class CosmosSyncDatabase { + + private final CosmosDatabase databaseWrapper; + private final CosmosSyncClient client; + private final String id; + + /** + * Instantiates a new Cosmos sync database. + * + * @param id the id + * @param client the client + * @param database the database + */ + CosmosSyncDatabase(String id, CosmosSyncClient client, CosmosDatabase database) { + this.id = id; + this.client = client; + this.databaseWrapper = database; + } + + /** + * Get the id of the CosmosDatabase + * + * @return the id of the database + */ + public String id() { + return id; + } + + /** + * Reads a database + * + * @return the {@link CosmosSyncDatabaseResponse} + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncDatabaseResponse read() throws CosmosClientException { + return client.mapDatabaseResponseAndBlock((databaseWrapper.read())); + } + + /** + * Reads a database. + * + * @param options the {@link CosmosDatabaseRequestOptions} request options. + * @return the {@link CosmosSyncDatabaseResponse} + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncDatabaseResponse read(CosmosDatabaseRequestOptions options) throws CosmosClientException { + return client.mapDatabaseResponseAndBlock(databaseWrapper.read(options)); + } + + /** + * Delete a database. + * + * @return the {@link CosmosSyncDatabaseResponse} + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncDatabaseResponse delete() throws CosmosClientException { + return client.mapDatabaseResponseAndBlock(databaseWrapper.delete()); + } + + /** + * Delete a database. + * + * @param options the {@link CosmosDatabaseRequestOptions} request options. + * @return the {@link CosmosSyncDatabaseResponse} + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncDatabaseResponse delete(CosmosDatabaseRequestOptions options) throws CosmosClientException { + return client.mapDatabaseResponseAndBlock(databaseWrapper.delete(options)); + } + + /* CosmosContainer operations */ + + /** + * Creates a cosmos container. + * + * @param containerProperties the {@link CosmosContainerProperties} + * @return the {@link CosmosSyncContainerResponse} with the created container. + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainer(CosmosContainerProperties containerProperties) throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainer(containerProperties)); + } + + /** + * Creates a cosmos container. + * + * @param containerProperties the {@link CosmosContainerProperties} + * @param throughput the throughput + * @return the {@link CosmosSyncContainerResponse} with the created container. + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainer(CosmosContainerProperties containerProperties, + int throughput) throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainer(containerProperties, throughput)); + } + + /** + * Creates a cosmos container. + * + * @param containerProperties the {@link CosmosContainerProperties} + * @param options the {@link CosmosContainerProperties} + * @return the {@link CosmosSyncContainerResponse} with the created container. + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainer(CosmosContainerProperties containerProperties, + CosmosContainerRequestOptions options) throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainer(containerProperties, options)); + } + + /** + * Creates a cosmos container. + * + * @param containerProperties the {@link CosmosContainerProperties} + * @param throughput the throughput + * @param options the {@link CosmosContainerProperties} + * @return the {@link CosmosSyncContainerResponse} with the created container. + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainer(CosmosContainerProperties containerProperties, + int throughput, + CosmosContainerRequestOptions options) throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainer(containerProperties, + throughput, + options)); + } + + /** + * Create container cosmos sync container response. + * + * @param id the id + * @param partitionKeyPath the partition key path + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainer(String id, String partitionKeyPath) throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainer(id, partitionKeyPath)); + } + + /** + * Create container cosmos sync container response. + * + * @param id the id + * @param partitionKeyPath the partition key path + * @param throughput the throughput + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainer(String id, String partitionKeyPath, int throughput) throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainer(id, partitionKeyPath, throughput)); + } + + /** + * Create container if not exists cosmos sync container response. + * + * @param containerProperties the container properties + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainerIfNotExists(CosmosContainerProperties containerProperties) + throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainerIfNotExists(containerProperties)); + } + + /** + * Create container if not exists cosmos sync container response. + * + * @param containerProperties the container properties + * @param throughput the throughput + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainerIfNotExists(CosmosContainerProperties containerProperties, + int throughput) throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainerIfNotExists(containerProperties, throughput)); + } + + /** + * Create container if not exists cosmos sync container response. + * + * @param id the id + * @param partitionKeyPath the partition key path + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainerIfNotExists(String id, + String partitionKeyPath) throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainerIfNotExists(id, partitionKeyPath)); + } + + /** + * Create container if not exists cosmos sync container response. + * + * @param id the id + * @param partitionKeyPath the partition key path + * @param throughput the throughput + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncContainerResponse createContainerIfNotExists(String id, String partitionKeyPath, + int throughput) throws CosmosClientException { + return this.mapContainerResponseAndBlock(databaseWrapper.createContainerIfNotExists(id, + partitionKeyPath, + throughput)); + } + + /** + * Map container response and block cosmos sync container response. + * + * @param containerMono the container mono + * @return the cosmos sync container response + * @throws CosmosClientException the cosmos client exception + */ + CosmosSyncContainerResponse mapContainerResponseAndBlock(Mono containerMono) + throws CosmosClientException { + try { + return containerMono + .map(this::convertResponse) + .block(); + } catch (Exception ex) { + final Throwable throwable = Exceptions.unwrap(ex); + if (throwable instanceof CosmosClientException) { + throw (CosmosClientException) throwable; + } else { + throw ex; + } + } + } + + /** + * Read all containers iterator. + * + * @param options the options + * @return the iterator + */ + public Iterator> readAllContainers(FeedOptions options) { + return databaseWrapper.readAllContainers(options) + .toIterable() + .iterator(); + } + + /** + * Read all containers iterator. + * + * @return the iterator + */ + public Iterator> readAllContainers() { + return databaseWrapper.readAllContainers() + .toIterable() + .iterator(); + } + + /** + * Query containers iterator. + * + * @param query the query + * @return the iterator + */ + public Iterator> queryContainers(String query) { + return databaseWrapper.queryContainers(query) + .toIterable() + .iterator(); + } + + /** + * Query containers iterator. + * + * @param query the query + * @param options the options + * @return the iterator + */ + public Iterator> queryContainers(String query, FeedOptions options) { + return databaseWrapper.queryContainers(query, options) + .toIterable() + .iterator(); + } + + /** + * Query containers iterator. + * + * @param querySpec the query spec + * @return the iterator + */ + public Iterator> queryContainers(SqlQuerySpec querySpec) { + return databaseWrapper.queryContainers(querySpec) + .toIterable() + .iterator(); + } + + /** + * Query containers iterator. + * + * @param querySpec the query spec + * @param options the options + * @return the iterator + */ + public Iterator> queryContainers(SqlQuerySpec querySpec, FeedOptions options) { + return databaseWrapper.queryContainers(querySpec, options) + .toIterable() + .iterator(); + } + + /** + * Gets a CosmosSyncContainer object without making a service call + * + * @param id id of the container + * @return Cosmos Container + */ + public CosmosSyncContainer getContainer(String id) { + return new CosmosSyncContainer(id, this, databaseWrapper.getContainer(id)); + } + + /** + * Convert response cosmos sync container response. + * + * @param response the response + * @return the cosmos sync container response + */ + /* */ + CosmosSyncContainerResponse convertResponse(CosmosContainerResponse response) { + return new CosmosSyncContainerResponse(response, this, client); + } + +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncDatabaseResponse.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncDatabaseResponse.java new file mode 100644 index 000000000000..c765a416cd0f --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncDatabaseResponse.java @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.CosmosDatabaseProperties; +import com.azure.data.cosmos.CosmosDatabaseResponse; + +public class CosmosSyncDatabaseResponse { + private final CosmosDatabaseResponse responseWrapper; + private final CosmosSyncDatabase database; + + CosmosSyncDatabaseResponse(CosmosDatabaseResponse response, CosmosSyncClient client) { + this.responseWrapper = response; + if (responseWrapper.database() != null) { + this.database = new CosmosSyncDatabase(responseWrapper.database().id(), client, responseWrapper.database()); + } else { + this.database = null; + } + } + + /** + * Gets the CosmosDatabase object + * + * @return {@link CosmosSyncDatabase} + */ + public CosmosSyncDatabase database() { + return database; + } + + /** + * Gets the cosmos database properties + * + * @return the cosmos database properties + */ + public CosmosDatabaseProperties properties() { + return responseWrapper.properties(); + } + + /** + * Gets the Max Quota. + * + * @return the database quota. + */ + public long databaseQuota() { + return responseWrapper.databaseQuota(); + } + + /** + * Gets the current Usage. + * + * @return the current database usage. + */ + public long databaseUsage() { + return responseWrapper.databaseUsage(); + } + +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncItem.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncItem.java new file mode 100644 index 000000000000..1c459cc69921 --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncItem.java @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.CosmosClientException; +import com.azure.data.cosmos.CosmosItem; +import com.azure.data.cosmos.CosmosItemRequestOptions; + +/** + * The Cosmos synchronous item. + */ +public class CosmosSyncItem { + private final CosmosSyncContainer container; + private final CosmosItem asyncItem; + private final String id; + private final Object partitionKey; + + /** + * Instantiates a new Cosmos sync item. + * + * @param id the id + * @param partitionKey the partition key + * @param cosmosSyncContainer the cosmos sync container + * @param item the item + */ + CosmosSyncItem(String id, Object partitionKey, CosmosSyncContainer cosmosSyncContainer, CosmosItem item) { + this.id = id; + this.partitionKey = partitionKey; + this.container = cosmosSyncContainer; + this.asyncItem = item; + } + + /** + * Id string. + * + * @return the string + */ + public String id() { + return id; + } + + /** + * Partition key object. + * + * @return the object + */ + public Object partitionKey() { + return partitionKey; + } + + /** + * Read cosmos sync item response. + * + * @param options the options + * @return the cosmos sync item response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncItemResponse read(CosmosItemRequestOptions options) throws CosmosClientException { + return container.mapItemResponseAndBlock(asyncItem.read(options)); + } + + /** + * Replace cosmos sync item response. + * + * @param item the item + * @param options the options + * @return the cosmos sync item response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncItemResponse replace(Object item, CosmosItemRequestOptions options) throws CosmosClientException { + return container.mapItemResponseAndBlock(asyncItem.replace(item, options)); + } + + /** + * Delete cosmos sync item response. + * + * @param options the options + * @return the cosmos sync item response + * @throws CosmosClientException the cosmos client exception + */ + public CosmosSyncItemResponse delete(CosmosItemRequestOptions options) throws CosmosClientException { + return container.mapItemResponseAndBlock(asyncItem.delete(options)); + } + + +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncItemResponse.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncItemResponse.java new file mode 100644 index 000000000000..3c379ccbaaef --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/CosmosSyncItemResponse.java @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.CosmosItemProperties; +import com.azure.data.cosmos.CosmosItemResponse; +import com.azure.data.cosmos.PartitionKey; + +public class CosmosSyncItemResponse { + private final CosmosItemResponse responseWrapper; + private final CosmosSyncItem item; + + + CosmosSyncItemResponse(CosmosItemResponse response, PartitionKey partitionKey, CosmosSyncContainer container) { + this.responseWrapper = response; + if (responseWrapper.item() != null) { + this.item = new CosmosSyncItem(responseWrapper.item().id(), partitionKey, container, responseWrapper.item()); + } else { + // Delete will have null container client in response + this.item = null; + } + } + + /** + * Gets the itemSettings + * + * @return the itemSettings + */ + public CosmosItemProperties properties() { + return responseWrapper.properties(); + } + + /** + * Gets the CosmosItem + * + * @return the cosmos item + */ + public CosmosSyncItem item() { + return item; + } +} \ No newline at end of file diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/package-info.java b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/package-info.java new file mode 100644 index 000000000000..3c6fba8deadd --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/main/java/com/azure/data/cosmos/sync/package-info.java @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** + * This package provides synchronous interfaces for interacting with Azure Cosmos DB. + */ +package com.azure.data.cosmos.sync; diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TestSuiteBase.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TestSuiteBase.java index cd83f3d4fb7d..c92ca920a9d8 100644 --- a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TestSuiteBase.java +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/rx/TestSuiteBase.java @@ -47,6 +47,8 @@ import com.azure.data.cosmos.internal.TestConfigurations; import com.azure.data.cosmos.internal.Utils; import com.azure.data.cosmos.internal.directconnectivity.Protocol; +import com.azure.data.cosmos.sync.CosmosSyncClient; +import com.azure.data.cosmos.sync.CosmosSyncDatabase; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -594,6 +596,16 @@ static protected CosmosDatabase createDatabase(CosmosClient client, String datab return client.createDatabase(databaseSettings).block().database(); } + static protected CosmosSyncDatabase createSyncDatabase(CosmosSyncClient client, String databaseId) { + CosmosDatabaseProperties databaseSettings = new CosmosDatabaseProperties(databaseId); + try { + return client.createDatabase(databaseSettings).database(); + } catch (CosmosClientException e) { + e.printStackTrace(); + } + return null; + } + static protected CosmosDatabase createDatabaseIfNotExists(CosmosClient client, String databaseId) { List res = client.queryDatabases(String.format("SELECT * FROM r where r.id = '%s'", databaseId), null) .flatMap(p -> Flux.fromIterable(p.results())) @@ -616,6 +628,16 @@ static protected void safeDeleteDatabase(CosmosDatabase database) { } } + static protected void safeDeleteSyncDatabase(CosmosSyncDatabase database) { + if (database != null) { + try { + database.delete(); + } catch (Exception e) { + logger.error("failed to delete sync database", e); + } + } + } + static protected void safeDeleteAllCollections(CosmosDatabase database) { if (database != null) { List collections = database.readAllContainers() @@ -669,6 +691,16 @@ static protected void safeClose(CosmosClient client) { } } + static protected void safeCloseSyncClient(CosmosSyncClient client) { + if (client != null) { + try { + client.close(); + } catch (Exception e) { + logger.error("failed to close client", e); + } + } + } + public void validateSuccess(Mono single, CosmosResponseValidator validator) throws InterruptedException { validateSuccess(single.flux(), validator, subscriberValidationTimeout); diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/sync/CosmosSyncContainerTest.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/sync/CosmosSyncContainerTest.java new file mode 100644 index 000000000000..99db89263599 --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/sync/CosmosSyncContainerTest.java @@ -0,0 +1,275 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + */ + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.CosmosClientBuilder; +import com.azure.data.cosmos.CosmosClientException; +import com.azure.data.cosmos.CosmosContainerProperties; +import com.azure.data.cosmos.CosmosContainerRequestOptions; +import com.azure.data.cosmos.CosmosDatabaseForTest; +import com.azure.data.cosmos.FeedOptions; +import com.azure.data.cosmos.FeedResponse; +import com.azure.data.cosmos.IndexingMode; +import com.azure.data.cosmos.IndexingPolicy; +import com.azure.data.cosmos.PartitionKeyDefinition; +import com.azure.data.cosmos.SqlQuerySpec; +import com.azure.data.cosmos.internal.HttpConstants; +import com.azure.data.cosmos.rx.TestSuiteBase; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CosmosSyncContainerTest extends TestSuiteBase { + + private String preExistingDatabaseId = CosmosDatabaseForTest.generateId(); + private List databases = new ArrayList<>(); + private CosmosSyncClient client; + private CosmosSyncDatabase createdDatabase; + + @Factory(dataProvider = "clientBuilders") + public CosmosSyncContainerTest(CosmosClientBuilder clientBuilder) { + super(clientBuilder); + } + + @BeforeClass(groups = {"emulator"}, timeOut = SETUP_TIMEOUT) + public void beforeClass() { + client = clientBuilder().buildSyncClient(); + createdDatabase = createSyncDatabase(client, preExistingDatabaseId); + } + + @AfterClass(groups = {"emulator"}, timeOut = SHUTDOWN_TIMEOUT, alwaysRun = true) + public void afterClass() { + safeDeleteSyncDatabase(createdDatabase); + for (String dbId : databases) { + safeDeleteSyncDatabase(client.getDatabase(dbId)); + } + safeCloseSyncClient(client); + } + + private CosmosContainerProperties getCollectionDefinition(String collectionName) { + PartitionKeyDefinition partitionKeyDef = new PartitionKeyDefinition(); + ArrayList paths = new ArrayList(); + paths.add("/mypk"); + partitionKeyDef.paths(paths); + + CosmosContainerProperties collectionDefinition = new CosmosContainerProperties( + collectionName, + partitionKeyDef); + + return collectionDefinition; + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void createContainer_withProperties() throws Exception { + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties); + validateContainerResponse(containerProperties, containerResponse); + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void createContainer_alreadyExists() throws Exception { + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties); + validateContainerResponse(containerProperties, containerResponse); + + try { + createdDatabase.createContainer(containerProperties); + } catch (Exception e) { + assertThat(e).isInstanceOf(CosmosClientException.class); + assertThat(((CosmosClientException) e).statusCode()).isEqualTo(HttpConstants.StatusCodes.CONFLICT); + } + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void createContainer_withThroughput() throws Exception { + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + int throughput = 1000; + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties, + throughput); + validateContainerResponse(containerProperties, containerResponse); + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void createContainer_withOptions() throws Exception { + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + CosmosContainerRequestOptions options = new CosmosContainerRequestOptions(); + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties, options); + validateContainerResponse(containerProperties, containerResponse); + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void createContainer_withThroughputAndOptions() throws Exception { + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + CosmosContainerRequestOptions options = new CosmosContainerRequestOptions(); + int throughput = 1000; + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties, + throughput, options); + validateContainerResponse(containerProperties, containerResponse); + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void createContainer_withNameAndPartitoinKeyPath() throws Exception { + String collectionName = UUID.randomUUID().toString(); + String partitionKeyPath = "/mypk"; + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(collectionName, partitionKeyPath); + validateContainerResponse(new CosmosContainerProperties(collectionName, partitionKeyPath), containerResponse); + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void createContainer_withNamePartitionPathAndThroughput() throws Exception { + String collectionName = UUID.randomUUID().toString(); + String partitionKeyPath = "/mypk"; + int throughput = 1000; + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(collectionName, + partitionKeyPath, throughput); + validateContainerResponse(new CosmosContainerProperties(collectionName, partitionKeyPath), containerResponse); + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void readContainer() throws Exception { + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + CosmosContainerRequestOptions options = new CosmosContainerRequestOptions(); + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties); + + CosmosSyncContainer syncContainer = createdDatabase.getContainer(collectionName); + + CosmosSyncContainerResponse read = syncContainer.read(); + validateContainerResponse(containerProperties, read); + + CosmosSyncContainerResponse read1 = syncContainer.read(options); + validateContainerResponse(containerProperties, read1); + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void deleteContainer() throws Exception { + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + CosmosContainerRequestOptions options = new CosmosContainerRequestOptions(); + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties); + + CosmosSyncContainer syncContainer = createdDatabase.getContainer(collectionName); + CosmosSyncContainerResponse deleteResponse = syncContainer.delete(); + + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void deleteContainer_withOptions() throws Exception { + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + CosmosContainerRequestOptions options = new CosmosContainerRequestOptions(); + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties); + + CosmosSyncContainer syncContainer = createdDatabase.getContainer(collectionName); + CosmosSyncContainerResponse deleteResponse = syncContainer.delete(options); + + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void replace() throws Exception { + + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + CosmosContainerRequestOptions options = new CosmosContainerRequestOptions(); + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties); + validateContainerResponse(containerProperties, containerResponse); + + assertThat(containerResponse.properties().indexingPolicy().indexingMode()).isEqualTo(IndexingMode.CONSISTENT); + + CosmosSyncContainerResponse replaceResponse = containerResponse.container() + .replace(containerResponse.properties().indexingPolicy( + new IndexingPolicy().indexingMode(IndexingMode.LAZY))); + assertThat(replaceResponse.properties().indexingPolicy().indexingMode()) + .isEqualTo(IndexingMode.LAZY); + + CosmosSyncContainerResponse replaceResponse1 = containerResponse.container() + .replace(containerResponse.properties().indexingPolicy( + new IndexingPolicy().indexingMode(IndexingMode.CONSISTENT)), + options); + assertThat(replaceResponse1.properties().indexingPolicy().indexingMode()) + .isEqualTo(IndexingMode.CONSISTENT); + + } + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void readAllContainers() throws Exception{ + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + CosmosContainerRequestOptions options = new CosmosContainerRequestOptions(); + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties); + Iterator> feedResponseIterator = createdDatabase.readAllContainers(); + // Very basic validation + assertThat(feedResponseIterator.hasNext()).isTrue(); + + FeedOptions feedOptions = new FeedOptions(); + Iterator> feedResponseIterator1 = createdDatabase.readAllContainers(feedOptions); + assertThat(feedResponseIterator1.hasNext()).isTrue(); + } + + + @Test(groups = { "emulator" }, timeOut = TIMEOUT) + public void queryContainer() throws Exception{ + String collectionName = UUID.randomUUID().toString(); + CosmosContainerProperties containerProperties = getCollectionDefinition(collectionName); + CosmosContainerRequestOptions options = new CosmosContainerRequestOptions(); + + CosmosSyncContainerResponse containerResponse = createdDatabase.createContainer(containerProperties); + String query = String.format("SELECT * from c where c.id = '%s'", collectionName); + FeedOptions feedOptions = new FeedOptions(); + + Iterator> feedResponseIterator = createdDatabase.queryContainers(query); + // Very basic validation + assertThat(feedResponseIterator.hasNext()).isTrue(); + + Iterator> feedResponseIterator1 = + createdDatabase.queryContainers(query, feedOptions); + // Very basic validation + assertThat(feedResponseIterator1.hasNext()).isTrue(); + + SqlQuerySpec querySpec = new SqlQuerySpec(query); + Iterator> feedResponseIterator2 = + createdDatabase.queryContainers(querySpec); + assertThat(feedResponseIterator2.hasNext()).isTrue(); + + Iterator> feedResponseIterator3 = + createdDatabase.queryContainers(querySpec, feedOptions); + assertThat(feedResponseIterator3.hasNext()).isTrue(); + } + + private void validateContainerResponse(CosmosContainerProperties containerProperties, + CosmosSyncContainerResponse createResponse) { + // Basic validation + assertThat(createResponse.properties().id()).isNotNull(); + assertThat(createResponse.properties().id()) + .as("check Resource Id") + .isEqualTo(containerProperties.id()); + + } +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/sync/CosmosSyncDatabaseTest.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/sync/CosmosSyncDatabaseTest.java new file mode 100644 index 000000000000..3dcbc2e0a7ee --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/sync/CosmosSyncDatabaseTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + */ + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.CosmosClientBuilder; +import com.azure.data.cosmos.CosmosClientException; +import com.azure.data.cosmos.CosmosDatabaseForTest; +import com.azure.data.cosmos.CosmosDatabaseProperties; +import com.azure.data.cosmos.CosmosDatabaseRequestOptions; +import com.azure.data.cosmos.FeedOptions; +import com.azure.data.cosmos.FeedResponse; +import com.azure.data.cosmos.SqlQuerySpec; +import com.azure.data.cosmos.internal.HttpConstants; +import com.azure.data.cosmos.rx.TestSuiteBase; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CosmosSyncDatabaseTest extends TestSuiteBase { + private String preExistingDatabaseId = CosmosDatabaseForTest.generateId(); + private List databases = new ArrayList<>(); + private CosmosSyncClient client; + private CosmosSyncDatabase createdDatabase; + + @Factory(dataProvider = "clientBuilders") + public CosmosSyncDatabaseTest(CosmosClientBuilder clientBuilder) { + super(clientBuilder); + } + + @BeforeClass(groups = {"emulator"}, timeOut = SETUP_TIMEOUT) + public void beforeClass() { + client = clientBuilder().buildSyncClient(); + createdDatabase = createSyncDatabase(client, preExistingDatabaseId); + } + + @AfterClass(groups = {"emulator"}, timeOut = SHUTDOWN_TIMEOUT, alwaysRun = true) + public void afterClass() { + safeDeleteSyncDatabase(createdDatabase); + for (String dbId : databases) { + safeDeleteSyncDatabase(client.getDatabase(dbId)); + } + safeCloseSyncClient(client); + } + + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void createDatabase_withPropertiesAndOptions() throws CosmosClientException { + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + databases.add(databaseDefinition.id()); + + CosmosSyncDatabaseResponse createResponse = client.createDatabase(databaseDefinition, + new CosmosDatabaseRequestOptions()); + + validateDatabaseResponse(databaseDefinition, createResponse); + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void createDatabase_withProperties() throws Exception { + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + databases.add(databaseDefinition.id()); + CosmosDatabaseProperties databaseProperties = new CosmosDatabaseProperties(databaseDefinition.id()); + + CosmosSyncDatabaseResponse createResponse = client.createDatabase(databaseProperties); + validateDatabaseResponse(databaseDefinition, createResponse); + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void createDatabase_alreadyExists() throws Exception { + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + databases.add(databaseDefinition.id()); + CosmosDatabaseProperties databaseProperties = new CosmosDatabaseProperties(databaseDefinition.id()); + + CosmosSyncDatabaseResponse createResponse = client.createDatabase(databaseProperties); + validateDatabaseResponse(databaseDefinition, createResponse); + try { + client.createDatabase(databaseProperties); + } catch (Exception e) { + assertThat(e).isInstanceOf(CosmosClientException.class); + assertThat(((CosmosClientException) e).statusCode()).isEqualTo(HttpConstants.StatusCodes.CONFLICT); + } + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void createDatabase_withId() throws Exception { + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + + CosmosSyncDatabaseResponse createResponse = client.createDatabase(databaseDefinition.id()); + validateDatabaseResponse(databaseDefinition, createResponse); + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void createDatabase_withPropertiesThroughputAndOptions() throws Exception { + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + CosmosDatabaseProperties databaseProperties = new CosmosDatabaseProperties(databaseDefinition.id()); + CosmosDatabaseRequestOptions requestOptions = new CosmosDatabaseRequestOptions(); + int throughput = 400; + try { + CosmosSyncDatabaseResponse createResponse = client.createDatabase(databaseProperties, throughput, requestOptions); + validateDatabaseResponse(databaseDefinition, createResponse); + } catch (CosmosClientException ex) { + assertThat(ex.statusCode()).isEqualTo(HttpConstants.StatusCodes.FORBIDDEN); + } + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void createDatabase_withPropertiesAndThroughput() throws Exception { + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + CosmosDatabaseProperties databaseProperties = new CosmosDatabaseProperties(databaseDefinition.id()); + int throughput = 1000; + try { + CosmosSyncDatabaseResponse createResponse = client.createDatabase(databaseProperties, throughput); + validateDatabaseResponse(databaseDefinition, createResponse); + } catch (Exception ex) { + if (ex instanceof CosmosClientException) { + assertThat(((CosmosClientException) ex).statusCode()).isEqualTo(HttpConstants.StatusCodes.FORBIDDEN); + } else { + throw ex; + } + } + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void createDatabase_withIdAndThroughput() throws Exception { + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + int throughput = 1000; + try { + CosmosSyncDatabaseResponse createResponse = client.createDatabase(databaseDefinition.id(), throughput); + validateDatabaseResponse(databaseDefinition, createResponse); + } catch (Exception ex) { + if (ex instanceof CosmosClientException) { + assertThat(((CosmosClientException) ex).statusCode()).isEqualTo(HttpConstants.StatusCodes.FORBIDDEN); + } else { + throw ex; + } + } + } + + @Test + public void readDatabase() throws Exception { + CosmosSyncDatabase database = client.getDatabase(createdDatabase.id()); + CosmosDatabaseProperties properties = new CosmosDatabaseProperties(createdDatabase.id()); + CosmosDatabaseRequestOptions options = new CosmosDatabaseRequestOptions(); + + CosmosSyncDatabaseResponse read = database.read(); + validateDatabaseResponse(properties, read); + + CosmosSyncDatabaseResponse read1 = database.read(options); + validateDatabaseResponse(properties, read1); + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void readAllDatabases() throws Exception { + FeedOptions options = new FeedOptions(); + options.maxItemCount(2); + + Iterator> readIterator = client.readAllDatabases(options); + // Basic validation + assertThat(readIterator.hasNext()).isTrue(); + + Iterator> readIterator1 = client.readAllDatabases(); + // Basic validation + assertThat(readIterator1.hasNext()).isTrue(); + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void queryAllDatabases() throws Exception { + FeedOptions options = new FeedOptions(); + options.maxItemCount(2); + String query = String.format("SELECT * from c where c.id = '%s'", createdDatabase.id()); + FeedOptions feedOptions = new FeedOptions(); + + Iterator> queryIterator = client.queryDatabases(query, options); + // Basic validation + assertThat(queryIterator.hasNext()).isTrue(); + + SqlQuerySpec querySpec = new SqlQuerySpec(query); + Iterator> queryIterator1 = client.queryDatabases(querySpec, options); + // Basic validation + assertThat(queryIterator1.hasNext()).isTrue(); + } + + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void deleteDatabase() throws Exception { + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + CosmosDatabaseProperties databaseProperties = new CosmosDatabaseProperties(databaseDefinition.id()); + CosmosSyncDatabaseResponse createResponse = client.createDatabase(databaseProperties); + + client.getDatabase(databaseDefinition.id()).delete(); + } + + @Test(groups = {"emulator"}, timeOut = TIMEOUT) + public void deleteDatabase_withOptions() throws Exception { + CosmosDatabaseProperties databaseDefinition = new CosmosDatabaseProperties(CosmosDatabaseForTest.generateId()); + CosmosDatabaseProperties databaseProperties = new CosmosDatabaseProperties(databaseDefinition.id()); + CosmosSyncDatabaseResponse createResponse = client.createDatabase(databaseProperties); + CosmosDatabaseRequestOptions options = new CosmosDatabaseRequestOptions(); + client.getDatabase(databaseDefinition.id()).delete(options); + } + + + private void validateDatabaseResponse(CosmosDatabaseProperties databaseDefinition, CosmosSyncDatabaseResponse createResponse) { + // Basic validation + assertThat(createResponse.properties().id()).isNotNull(); + assertThat(createResponse.properties().id()) + .as("check Resource Id") + .isEqualTo(databaseDefinition.id()); + + } +} diff --git a/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/sync/CosmosSyncItemTest.java b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/sync/CosmosSyncItemTest.java new file mode 100644 index 000000000000..095c8bad2e5e --- /dev/null +++ b/sdk/cosmos/microsoft-azure-cosmos/src/test/java/com/azure/data/cosmos/sync/CosmosSyncItemTest.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + * + */ + +package com.azure.data.cosmos.sync; + +import com.azure.data.cosmos.BridgeInternal; +import com.azure.data.cosmos.CosmosClientBuilder; +import com.azure.data.cosmos.CosmosClientException; +import com.azure.data.cosmos.CosmosContainer; +import com.azure.data.cosmos.CosmosDatabaseForTest; +import com.azure.data.cosmos.CosmosItemProperties; +import com.azure.data.cosmos.CosmosItemRequestOptions; +import com.azure.data.cosmos.FeedOptions; +import com.azure.data.cosmos.FeedResponse; +import com.azure.data.cosmos.PartitionKey; +import com.azure.data.cosmos.SqlQuerySpec; +import com.azure.data.cosmos.internal.HttpConstants; +import com.azure.data.cosmos.rx.TestSuiteBase; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Factory; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CosmosSyncItemTest extends TestSuiteBase { + + private String preExistingDatabaseId = CosmosDatabaseForTest.generateId(); + private List databases = new ArrayList<>(); + private CosmosSyncClient client; + private CosmosSyncContainer container; + + @Factory(dataProvider = "clientBuilders") + public CosmosSyncItemTest(CosmosClientBuilder clientBuilder) { + super(clientBuilder); + } + + + @BeforeClass(groups = {"simple"}, timeOut = SETUP_TIMEOUT) + public void beforeClass() { + assertThat(this.client).isNull(); + this.client = clientBuilder().buildSyncClient(); + CosmosContainer asyncContainer = getSharedMultiPartitionCosmosContainer(this.client.asyncClient()); + container = client.getDatabase(asyncContainer.getDatabase().id()).getContainer(asyncContainer.id()); + } + + @AfterClass(groups = {"simple"}, timeOut = SHUTDOWN_TIMEOUT, alwaysRun = true) + public void afterClass() { + assertThat(this.client).isNotNull(); + this.client.close(); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT) + public void createItem() throws Exception { + CosmosItemProperties properties = getDocumentDefinition(UUID.randomUUID().toString()); + CosmosSyncItemResponse itemResponse = container.createItem(properties); + validateItemResponse(properties, itemResponse); + + properties = getDocumentDefinition(UUID.randomUUID().toString()); + CosmosSyncItemResponse itemResponse1 = container.createItem(properties, new CosmosItemRequestOptions()); + validateItemResponse(properties, itemResponse1); + + } + + @Test(groups = {"simple"}, timeOut = TIMEOUT) + public void createItem_alreadyExists() throws Exception { + CosmosItemProperties properties = getDocumentDefinition(UUID.randomUUID().toString()); + CosmosSyncItemResponse itemResponse = container.createItem(properties); + validateItemResponse(properties, itemResponse); + + properties = getDocumentDefinition(UUID.randomUUID().toString()); + CosmosSyncItemResponse itemResponse1 = container.createItem(properties, new CosmosItemRequestOptions()); + validateItemResponse(properties, itemResponse1); + + // Test for conflict + try { + container.createItem(properties, new CosmosItemRequestOptions()); + } catch (Exception e) { + assertThat(e).isInstanceOf(CosmosClientException.class); + assertThat(((CosmosClientException) e).statusCode()).isEqualTo(HttpConstants.StatusCodes.CONFLICT); + } + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT) + public void readItem() throws Exception { + CosmosItemProperties properties = getDocumentDefinition(UUID.randomUUID().toString()); + CosmosSyncItemResponse itemResponse = container.createItem(properties); + + CosmosSyncItemResponse readResponse1 = itemResponse.item() + .read(new CosmosItemRequestOptions() + .partitionKey(new PartitionKey(properties.get("mypk")))); + validateItemResponse(properties, readResponse1); + + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT) + public void replaceItem() throws Exception{ + CosmosItemProperties properties = getDocumentDefinition(UUID.randomUUID().toString()); + CosmosSyncItemResponse itemResponse = container.createItem(properties); + + validateItemResponse(properties, itemResponse); + String newPropValue = UUID.randomUUID().toString(); + BridgeInternal.setProperty(properties, "newProp", newPropValue); + CosmosItemRequestOptions options = new CosmosItemRequestOptions(); + options.partitionKey(new PartitionKey(properties.get("mypk"))); + // replace document + CosmosSyncItemResponse replace = itemResponse.item().replace(properties, options); + assertThat(replace.properties().get("newProp")).isEqualTo(newPropValue); + } + + @Test(groups = { "simple" }, timeOut = TIMEOUT) + public void deleteItem() throws Exception { + CosmosItemProperties properties = getDocumentDefinition(UUID.randomUUID().toString()); + CosmosSyncItemResponse itemResponse = container.createItem(properties); + CosmosItemRequestOptions options = new CosmosItemRequestOptions(); + options.partitionKey(new PartitionKey(properties.get("mypk"))); + + CosmosSyncItemResponse deleteResponse = itemResponse.item().delete(options); + assertThat(deleteResponse.item()).isNull(); + + } + + + @Test(groups = { "simple" }, timeOut = TIMEOUT) + public void readAllItems() throws Exception{ + CosmosItemProperties properties = getDocumentDefinition(UUID.randomUUID().toString()); + CosmosSyncItemResponse itemResponse = container.createItem(properties); + + FeedOptions feedOptions = new FeedOptions(); + feedOptions.enableCrossPartitionQuery(true); + Iterator> feedResponseIterator3 = + container.readAllItems(feedOptions); + assertThat(feedResponseIterator3.hasNext()).isTrue(); + } + + + @Test(groups = { "simple" }, timeOut = TIMEOUT) + public void queryItems() throws Exception{ + CosmosItemProperties properties = getDocumentDefinition(UUID.randomUUID().toString()); + CosmosSyncItemResponse itemResponse = container.createItem(properties); + + String query = String.format("SELECT * from c where c.id = '%s'", properties.id()); + FeedOptions feedOptions = new FeedOptions().enableCrossPartitionQuery(true); + + Iterator> feedResponseIterator1 = + container.queryItems(query, feedOptions); + // Very basic validation + assertThat(feedResponseIterator1.hasNext()).isTrue(); + + SqlQuerySpec querySpec = new SqlQuerySpec(query); + Iterator> feedResponseIterator3 = + container.queryItems(querySpec, feedOptions); + assertThat(feedResponseIterator3.hasNext()).isTrue(); + } + + + private CosmosItemProperties getDocumentDefinition(String documentId) { + final String uuid = UUID.randomUUID().toString(); + final CosmosItemProperties properties = + new CosmosItemProperties(String.format("{ " + + "\"id\": \"%s\", " + + "\"mypk\": \"%s\", " + + "\"sgmts\": [[6519456, 1471916863], [2498434, 1455671440]]" + + "}" + , documentId, uuid)); + return properties; + } + + private void validateItemResponse(CosmosItemProperties containerProperties, + CosmosSyncItemResponse createResponse) { + // Basic validation + assertThat(createResponse.properties().id()).isNotNull(); + assertThat(createResponse.properties().id()) + .as("check Resource Id") + .isEqualTo(containerProperties.id()); + } + +}