diff --git a/src/main/java/org/opengroup/osdu/azure/cosmosdb/CosmosStore.java b/src/main/java/org/opengroup/osdu/azure/cosmosdb/CosmosStore.java index e57c373b9640fe195eacdd0d7b3f3b4cdbce5eda..1d704abd235f90445747001959c0148a4c8c0ea9 100644 --- a/src/main/java/org/opengroup/osdu/azure/cosmosdb/CosmosStore.java +++ b/src/main/java/org/opengroup/osdu/azure/cosmosdb/CosmosStore.java @@ -26,7 +26,7 @@ import com.azure.cosmos.models.SqlQuerySpec; import com.azure.cosmos.util.CosmosPagedIterable; import org.apache.http.HttpStatus; import org.opengroup.osdu.azure.logging.CoreLoggerFactory; -import org.opengroup.osdu.azure.logging.DependencyPayload; +import org.opengroup.osdu.azure.logging.DependencyLogger; import org.opengroup.osdu.azure.query.CosmosStorePageRequest; import org.opengroup.osdu.core.common.model.http.AppException; import org.springframework.beans.factory.annotation.Autowired; @@ -35,12 +35,13 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.stereotype.Component; -import java.time.Duration; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Optional; +import static org.opengroup.osdu.azure.logging.DependencyType.COSMOS_STORE; + /** * A simpler interface for interacting with CosmosDB. * Usage Examples: @@ -86,6 +87,8 @@ public class CosmosStore { @Autowired private ICosmosClientFactory cosmosClientFactory; + @Autowired + private DependencyLogger dependencyLogger; /** * @param dataPartitionId Data partition id @@ -109,12 +112,12 @@ public class CosmosStore { } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param id ID of item - * @param partitionKey Partition key of item - * @param clazz Class to serialize results into - * @param Type to return + * @param cosmosDBName Database name + * @param collection Collection name + * @param id ID of item + * @param partitionKey Partition key of item + * @param clazz Class to serialize results into + * @param Type to return * @return The item */ public Optional findItem( @@ -146,11 +149,11 @@ public class CosmosStore { } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param id ID of item - * @param partitionKey Partition key of item - * @param Type of item + * @param cosmosDBName Database name + * @param collection Collection name + * @param id ID of item + * @param partitionKey Partition key of item + * @param Type of item */ public void deleteItem( final String cosmosDBName, @@ -180,11 +183,11 @@ public class CosmosStore { } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param partitionKey Partition key of item - * @param item Data object to store - * @param Type of item + * @param cosmosDBName Database name + * @param collection Collection name + * @param partitionKey Partition key of item + * @param item Data object to store + * @param Type of item */ public void upsertItem( final String cosmosDBName, @@ -218,6 +221,7 @@ public class CosmosStore { PartitionKey key = new PartitionKey(partitionKey); CosmosItemRequestOptions options = new CosmosItemRequestOptions(); cosmosContainer.replaceItem(item, id, key, options); + CoreLoggerFactory.getInstance().getLogger(LOGGER_NAME).debug(String.format("REPLACE_ITEM with id=%s and partition_key=%s", id, partitionKey)); } catch (NotFoundException e) { statusCode = e.getStatusCode(); String errorMessage = "Item was unexpectedly not found"; @@ -232,7 +236,7 @@ public class CosmosStore { final long timeTaken = System.currentTimeMillis() - start; final String dependencyTarget = getDependencyTarget(dataPartitionId, cosmosDBName, collection); final String dependencyData = String.format("id=%s partition_key=%s", id, partitionKey); - logDependency("REPLACE_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); + dependencyLogger.logDependency(COSMOS_STORE, "REPLACE_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); } } @@ -255,11 +259,11 @@ public class CosmosStore { } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param partitionKey Partition key of item - * @param item Data object to store - * @param Type of item + * @param cosmosDBName Database name + * @param collection Collection name + * @param partitionKey Partition key of item + * @param item Data object to store + * @param Type of item */ public void createItem( final String cosmosDBName, @@ -311,12 +315,12 @@ public class CosmosStore { } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param query {@link SqlQuerySpec} to execute - * @param options Options - * @param clazz Class type of response - * @param Type + * @param cosmosDBName Database name + * @param collection Collection name + * @param query {@link SqlQuerySpec} to execute + * @param options Options + * @param clazz Class type of response + * @param Type * @return List List of items found on specific page in container */ public List queryItems( @@ -411,20 +415,20 @@ public class CosmosStore { final String dependencyTarget = getDependencyTarget(dataPartitionId, cosmosDBName, collection); final String dependencyData = String.format("query=%s", query.getQueryText()); CoreLoggerFactory.getInstance().getLogger(LOGGER_NAME).debug("Done. Retrieved {} results", results.size()); - logDependency("QUERY_ITEMS_PAGE", dependencyData, dependencyTarget, timeTaken, HttpStatus.SC_OK, true); + dependencyLogger.logDependency(COSMOS_STORE, "QUERY_ITEMS_PAGE", dependencyData, dependencyTarget, timeTaken, HttpStatus.SC_OK, true); CosmosStorePageRequest pageRequest = CosmosStorePageRequest.of(currentPageNumber, pageSize, internalcontinuationToken); return new PageImpl(results, pageRequest, documentNumber); } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param container Cosmos container - * @param id ID of item - * @param partitionKey Partition key of item - * @param clazz Class to serialize results into - * @param Type to return + * @param cosmosDBName Database name + * @param collection Collection name + * @param container Cosmos container + * @param id ID of item + * @param partitionKey Partition key of item + * @param clazz Class to serialize results into + * @param Type to return * @return The item */ private Optional findItemInternal( @@ -453,19 +457,19 @@ public class CosmosStore { throw new AppException(500, errorMessage, e.getMessage(), e); } finally { final long timeTaken = System.currentTimeMillis() - start; - final String dependencyTarget = getDependencyTarget(cosmosDBName, collection); + final String dependencyTarget = DependencyLogger.getCosmosDependencyTarget(cosmosDBName, collection); final String dependencyData = String.format("id=%s partition_key=%s", id, partitionKey); - logDependency("READ_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); + dependencyLogger.logDependency(COSMOS_STORE, "READ_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); } } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param container Cosmos container - * @param id ID of item - * @param partitionKey Partition key of item - * @param Type of item + * @param cosmosDBName Database name + * @param collection Collection name + * @param container Cosmos container + * @param id ID of item + * @param partitionKey Partition key of item + * @param Type of item */ private void deleteItemInternal( final String cosmosDBName, @@ -492,19 +496,19 @@ public class CosmosStore { throw new AppException(500, errorMessage, e.getMessage(), e); } finally { final long timeTaken = System.currentTimeMillis() - start; - final String dependencyTarget = getDependencyTarget(cosmosDBName, collection); + final String dependencyTarget = DependencyLogger.getCosmosDependencyTarget(cosmosDBName, collection); final String dependencyData = String.format("id=%s partition_key=%s", id, partitionKey); - logDependency("DELETE_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); + dependencyLogger.logDependency(COSMOS_STORE, "DELETE_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); } } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param container Cosmos container. - * @param partitionKey Partition key of item - * @param item Data object to store - * @param Type of item + * @param cosmosDBName Database name + * @param collection Collection name + * @param container Cosmos container. + * @param partitionKey Partition key of item + * @param item Data object to store + * @param Type of item */ private void upsertItemInternal( final String cosmosDBName, @@ -526,19 +530,19 @@ public class CosmosStore { throw new AppException(500, errorMessage, e.getMessage(), e); } finally { final long timeTaken = System.currentTimeMillis() - start; - final String dependencyTarget = getDependencyTarget(cosmosDBName, collection); + final String dependencyTarget = DependencyLogger.getCosmosDependencyTarget(cosmosDBName, collection); final String dependencyData = String.format("partition_key=%s", partitionKey); - logDependency("UPSERT_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); + dependencyLogger.logDependency(COSMOS_STORE, "UPSERT_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); } } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param container Cosmos container - * @param partitionKey Partition key of item - * @param item Data object to store - * @param Type of item + * @param cosmosDBName Database name + * @param collection Collection name + * @param container Cosmos container + * @param partitionKey Partition key of item + * @param item Data object to store + * @param Type of item */ private void createItemInternal( final String cosmosDBName, @@ -565,20 +569,20 @@ public class CosmosStore { throw new AppException(500, errorMessage, e.getMessage(), e); } finally { final long timeTaken = System.currentTimeMillis() - start; - final String dependencyTarget = getDependencyTarget(cosmosDBName, collection); + final String dependencyTarget = DependencyLogger.getCosmosDependencyTarget(cosmosDBName, collection); final String dependencyData = String.format("partition_key=%s", partitionKey); - logDependency("CREATE_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); + dependencyLogger.logDependency(COSMOS_STORE, "CREATE_ITEM", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); } } /** - * @param cosmosDBName Database name - * @param collection Collection name - * @param container Cosmos container - * @param query {@link SqlQuerySpec} to execute - * @param options Options - * @param clazz Class type of response - * @param Type + * @param cosmosDBName Database name + * @param collection Collection name + * @param container Cosmos container + * @param query {@link SqlQuerySpec} to execute + * @param options Options + * @param clazz Class type of response + * @param Type * @return List List of items found on specific page in container */ private List queryItemsInternal( @@ -598,10 +602,10 @@ public class CosmosStore { results.addAll(cosmosItemPropertiesFeedResponse.getResults()); }); final long timeTaken = System.currentTimeMillis() - start; - final String dependencyTarget = getDependencyTarget(cosmosDBName, collection); + final String dependencyTarget = DependencyLogger.getCosmosDependencyTarget(cosmosDBName, collection); final String dependencyData = String.format("query=%s", query.getQueryText()); CoreLoggerFactory.getInstance().getLogger(LOGGER_NAME).debug("Done. Retrieved {} results", results.size()); - logDependency("QUERY_ITEMS", dependencyData, dependencyTarget, timeTaken, HttpStatus.SC_OK, true); + dependencyLogger.logDependency(COSMOS_STORE, "QUERY_ITEMS", dependencyData, dependencyTarget, timeTaken, HttpStatus.SC_OK, true); return results; } @@ -628,8 +632,8 @@ public class CosmosStore { } /** - * @param cosmosDBName Database name - * @param collection Collection name + * @param cosmosDBName Database name + * @param collection Collection name * @return Cosmos container */ private CosmosContainer getSystemCosmosContainer( @@ -646,37 +650,6 @@ public class CosmosStore { } } - /** - * Logs and returns instance of AppException. - * - * @param status Response status code - * @param errorMessage Error message - * @param e Original exception - * @return Instance of AppException - */ - private AppException handleCosmosStoreException(final int status, final String errorMessage, final Exception e) { - CoreLoggerFactory.getInstance().getLogger(LOGGER_NAME).warn(errorMessage, e); - return new AppException(status, errorMessage, e.getMessage(), e); - } - - /** - * Log dependency. - * - * @param name the name of the command initiated with this dependency call - * @param data the command initiated by this dependency call - * @param target the target of this dependency call - * @param timeTakenInMs the request duration in milliseconds - * @param resultCode the result code of the call - * @param success indication of successful or unsuccessful call - */ - private void logDependency(final String name, final String data, final String target, final long timeTakenInMs, final int resultCode, final boolean success) { - DependencyPayload payload = new DependencyPayload(name, data, Duration.ofMillis(timeTakenInMs), String.valueOf(resultCode), success); - payload.setType("CosmosStore"); - payload.setTarget(target); - - CoreLoggerFactory.getInstance().getLogger(LOGGER_NAME).logDependency(payload); - } - /** * Return a string composed of partition ID, database name and collection. * @@ -690,13 +663,15 @@ public class CosmosStore { } /** - * Return a string composed of database name and collection. + * Logs and returns instance of AppException. * - * @param databaseName the Cosmos database name - * @param collection the Cosmos collection name - * @return the dependency target string + * @param status Response status code + * @param errorMessage Error message + * @param e Original exception + * @return Instance of AppException */ - private String getDependencyTarget(final String databaseName, final String collection) { - return String.format("%s/%s", databaseName, collection); + private AppException handleCosmosStoreException(final int status, final String errorMessage, final Exception e) { + CoreLoggerFactory.getInstance().getLogger(LOGGER_NAME).warn(errorMessage, e); + return new AppException(status, errorMessage, e.getMessage(), e); } } diff --git a/src/main/java/org/opengroup/osdu/azure/cosmosdb/CosmosStoreBulkOperations.java b/src/main/java/org/opengroup/osdu/azure/cosmosdb/CosmosStoreBulkOperations.java index 275070c93dcface498572ced9c850fcf7f2af9f2..b1dcce61ce61787fcda3772d94d8f70aa46a4869 100644 --- a/src/main/java/org/opengroup/osdu/azure/cosmosdb/CosmosStoreBulkOperations.java +++ b/src/main/java/org/opengroup/osdu/azure/cosmosdb/CosmosStoreBulkOperations.java @@ -11,6 +11,8 @@ import com.google.gson.Gson; import com.microsoft.azure.documentdb.DocumentClientException; import com.microsoft.azure.documentdb.bulkexecutor.BulkImportResponse; import com.microsoft.azure.documentdb.bulkexecutor.DocumentBulkExecutor; +import org.apache.http.HttpStatus; +import org.opengroup.osdu.azure.logging.DependencyLogger; import org.opengroup.osdu.core.common.model.http.AppException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,8 +22,11 @@ import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import static org.opengroup.osdu.azure.logging.DependencyType.COSMOS_STORE; + /** * Class to perform bulk Cosmos operations using DocumentBulkExecutor. */ @@ -31,6 +36,9 @@ public class CosmosStoreBulkOperations { private static final Logger LOGGER = LoggerFactory.getLogger(CosmosStoreBulkOperations.class.getName()); + @Autowired + private DependencyLogger dependencyLogger; + @Autowired private ICosmosBulkExecutorFactory bulkExecutorFactory; @@ -59,6 +67,8 @@ public class CosmosStoreBulkOperations { final int maxConcurrencyPerPartitionRange) { Collection serializedDocuments = new ArrayList<>(); Gson gson = new Gson(); + final long start = System.currentTimeMillis(); + int statusCode = HttpStatus.SC_OK; // Serialize documents to json strings for (T item : documents) { @@ -75,9 +85,15 @@ public class CosmosStoreBulkOperations { } return response; } catch (DocumentClientException e) { + statusCode = HttpStatus.SC_INTERNAL_SERVER_ERROR; String errorMessage = "Unexpectedly failed to bulk insert documents"; LOGGER.warn(errorMessage, e); - throw new AppException(500, errorMessage, e.getMessage(), e); + throw new AppException(statusCode, errorMessage, e.getMessage(), e); + } finally { + final long timeTaken = System.currentTimeMillis() - start; + final String dependencyTarget = DependencyLogger.getCosmosDependencyTarget(cosmosDBName, collectionName); + final String dependencyData = String.format("collectionName=%s", collectionName); + dependencyLogger.logDependency(COSMOS_STORE, "UPSERT_ITEMS", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); } } @@ -98,6 +114,8 @@ public class CosmosStoreBulkOperations { final List docs, final List partitionKeys, final int maxConcurrencyPerPartitionRange) { + final long start = System.currentTimeMillis(); + int statusCode = HttpStatus.SC_OK; try { List exceptions = new ArrayList<>(); @@ -130,13 +148,20 @@ public class CosmosStoreBulkOperations { }); if (!exceptions.isEmpty()) { + statusCode = HttpStatus.SC_INTERNAL_SERVER_ERROR; LOGGER.error("Failed to create documents in CosmosDB: {}", String.join(",", exceptions)); - throw new AppException(500, "Record creation has failed!", "Failed to create documents in CosmosDB", exceptions.toArray(new String[exceptions.size()])); + throw new AppException(statusCode, "Record creation has failed!", "Failed to create documents in CosmosDB", exceptions.toArray(new String[exceptions.size()])); } } catch (Exception e) { + statusCode = HttpStatus.SC_INTERNAL_SERVER_ERROR; String errorMessage = "Unexpectedly failed to bulk insert documents"; LOGGER.error(errorMessage, e); - throw new AppException(500, errorMessage, e.getMessage(), e); + throw new AppException(statusCode, errorMessage, e.getMessage(), e); + } finally { + final long timeTaken = System.currentTimeMillis() - start; + final String dependencyTarget = DependencyLogger.getCosmosDependencyTarget(cosmosDBName, collectionName); + final String dependencyData = String.format("partition_key=%s", new HashSet<>(partitionKeys)); + dependencyLogger.logDependency(COSMOS_STORE, "UPSERT_ITEMS", dependencyData, dependencyTarget, timeTaken, statusCode, statusCode == HttpStatus.SC_OK); } } } diff --git a/src/main/java/org/opengroup/osdu/azure/logging/DependencyLogger.java b/src/main/java/org/opengroup/osdu/azure/logging/DependencyLogger.java new file mode 100644 index 0000000000000000000000000000000000000000..08064eca727643f315b509b0e495ec4d4714d5d2 --- /dev/null +++ b/src/main/java/org/opengroup/osdu/azure/logging/DependencyLogger.java @@ -0,0 +1,44 @@ +package org.opengroup.osdu.azure.logging; + +import org.springframework.stereotype.Component; + +import java.time.Duration; + +/** + * Dependency logger. + */ +@Component +public class DependencyLogger { + + private static final String LOGGER_NAME = DependencyLogger.class.getName(); + + /** + * Log dependency. + * + * @param type the dependency type + * @param name the name of the command initiated with this dependency call + * @param data the command initiated by this dependency call + * @param target the target of this dependency call + * @param timeTakenInMs the request duration in milliseconds + * @param resultCode the result code of the call + * @param success indication of successful or unsuccessful call + */ + public void logDependency(final String type, final String name, final String data, final String target, final long timeTakenInMs, final int resultCode, final boolean success) { + DependencyPayload payload = new DependencyPayload(name, data, Duration.ofMillis(timeTakenInMs), String.valueOf(resultCode), success); + payload.setType(type); + payload.setTarget(target); + + CoreLoggerFactory.getInstance().getLogger(LOGGER_NAME).logDependency(payload); + } + + /** + * Return a string composed of database name and collection. + * + * @param databaseName the Cosmos database name + * @param collection the Cosmos collection name + * @return the dependency target string + */ + public static String getCosmosDependencyTarget(final String databaseName, final String collection) { + return String.format("%s/%s", databaseName, collection); + } +} diff --git a/src/main/java/org/opengroup/osdu/azure/logging/DependencyType.java b/src/main/java/org/opengroup/osdu/azure/logging/DependencyType.java new file mode 100644 index 0000000000000000000000000000000000000000..611733b6ef2d6b32ecc7ca858bfdb8bb69fef202 --- /dev/null +++ b/src/main/java/org/opengroup/osdu/azure/logging/DependencyType.java @@ -0,0 +1,15 @@ +package org.opengroup.osdu.azure.logging; + +/** + * Dependency type. + */ +public final class DependencyType { + + /** + * Default private constructor. + */ + private DependencyType() { + } + + public static final String COSMOS_STORE = "CosmosStore"; +} diff --git a/src/test/java/org/opengroup/osdu/azure/cosmosdb/ComosBulkExecutorImplTest.java b/src/test/java/org/opengroup/osdu/azure/cosmosdb/ComosBulkExecutorImplTest.java deleted file mode 100644 index d1db85e3fcda317992c9c892d71420ece6697ac5..0000000000000000000000000000000000000000 --- a/src/test/java/org/opengroup/osdu/azure/cosmosdb/ComosBulkExecutorImplTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.opengroup.osdu.azure.cosmosdb; - -import com.microsoft.azure.documentdb.bulkexecutor.DocumentBulkExecutor; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.opengroup.osdu.azure.partition.PartitionServiceClient; - -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.*; -import static org.mockito.Mockito.never; -import static org.mockito.MockitoAnnotations.initMocks; - -@ExtendWith(MockitoExtension.class) -public class ComosBulkExecutorImplTest { - - @Mock - private Map cosmosClientMap; - @Mock - private PartitionServiceClient partitionService; - @InjectMocks - private CosmosBulkExecutorFactoryImpl sut; - - private static final String PARTITION_ID = "dataPartitionId"; - private static final String COSMOS_DB_NAME = "cosmosDBName"; - private static final String COSMOS_COLLECTION_NAME = "cosmosCollectionName"; - - @BeforeEach - void init() { - initMocks(this); - } - - @Test - public void should_throwException_given_nullDataPartitionId() { - try { - this.sut.getClient(null, COSMOS_DB_NAME, COSMOS_COLLECTION_NAME); - } catch (NullPointerException ex) { - assertEquals("dataPartitionId cannot be null!", ex.getMessage()); - } catch (Exception ex) { - fail("Should not get any other exception. Received " + ex.getClass()); - } - } - - @Test - public void should_throwException_given_emptyDataPartitionId() { - try { - this.sut.getClient("", COSMOS_DB_NAME, COSMOS_COLLECTION_NAME); - } catch (IllegalArgumentException ex) { - assertEquals("dataPartitionId cannot be empty!", ex.getMessage()); - } catch (Exception ex) { - fail("Should not get any other exception. Received " + ex.getClass()); - } - } - - @Test - public void should_return_cachedClient_when_cachedEarlier() { - DocumentBulkExecutor cosmosClient = mock(DocumentBulkExecutor.class); - final String cacheKey = String.format("%s-%s-%s-cosmosBulkExecutor", PARTITION_ID, COSMOS_DB_NAME, COSMOS_COLLECTION_NAME); - when(this.cosmosClientMap.containsKey(cacheKey)).thenReturn(true); - when(this.cosmosClientMap.get(cacheKey)).thenReturn(cosmosClient); - - this.sut.getClient(PARTITION_ID, COSMOS_DB_NAME, COSMOS_COLLECTION_NAME); - verify(this.partitionService, never()).getPartition(PARTITION_ID); - } - -} diff --git a/src/test/java/org/opengroup/osdu/azure/cosmosdb/CosmosBulkExecutorImplTest.java b/src/test/java/org/opengroup/osdu/azure/cosmosdb/CosmosBulkExecutorImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1310c5cea5931c416b9a2a3a2ffd6af4dd43f3fe --- /dev/null +++ b/src/test/java/org/opengroup/osdu/azure/cosmosdb/CosmosBulkExecutorImplTest.java @@ -0,0 +1,72 @@ +package org.opengroup.osdu.azure.cosmosdb; + +import com.google.gson.Gson; +import com.microsoft.azure.documentdb.DocumentClientException; +import com.microsoft.azure.documentdb.bulkexecutor.BulkImportResponse; +import com.microsoft.azure.documentdb.bulkexecutor.DocumentBulkExecutor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.opengroup.osdu.azure.logging.DependencyLogger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static java.util.Collections.singletonList; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; +import static org.opengroup.osdu.azure.logging.DependencyType.COSMOS_STORE; + +@ExtendWith(MockitoExtension.class) +public class CosmosBulkExecutorImplTest { + + private static final String COSMOS_DB = "cosmosdb"; + private static final String COLLECTION = "collection"; + private static final String DATA_PARTITION_ID = "data-partition-id"; + private static final String ITEM = "ITEM"; + + private Gson gson = new Gson(); + + @Mock + private DependencyLogger dependencyLogger; + @Mock + private ICosmosClientFactory cosmosClientFactory; + @Mock + private ICosmosBulkExecutorFactory bulkExecutorFactory; + @InjectMocks + private CosmosStoreBulkOperations sut; + + @BeforeEach + void init() { + openMocks(this); + } + + @Test + public void bulkUpsert_Success() throws DocumentClientException { + DocumentBulkExecutor documentBulkExecutor = mock(DocumentBulkExecutor.class); + BulkImportResponse bulkImportResponse = mock(BulkImportResponse.class); + List documents = singletonList(ITEM); + Collection serializedDocuments = new ArrayList<>(); + for (String item : documents) { + String serializedDocument = gson.toJson(item); + serializedDocuments.add(serializedDocument); + } + lenient().doReturn(bulkImportResponse).when(documentBulkExecutor).importAll(serializedDocuments, false, false, 1); + when(this.bulkExecutorFactory.getClient(DATA_PARTITION_ID, COSMOS_DB, COLLECTION)).thenReturn(documentBulkExecutor); + + this.sut.bulkInsert(DATA_PARTITION_ID, COSMOS_DB, COLLECTION, documents, false, false, 1); + + verify(this.bulkExecutorFactory, times(1)).getClient(DATA_PARTITION_ID, COSMOS_DB, COLLECTION); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("UPSERT_ITEMS"), eq("collectionName=collection"), eq("cosmosdb/collection"), anyLong(), eq(200), eq(true)); + } +} diff --git a/src/test/java/org/opengroup/osdu/azure/cosmosdb/CosmosStoreTest.java b/src/test/java/org/opengroup/osdu/azure/cosmosdb/CosmosStoreTest.java index 367fd6e44ca1488084759c650a7790d8843c5bdd..53d5ffd0ecf8d72a90ab808fc29d5c6b3eef0f2c 100644 --- a/src/test/java/org/opengroup/osdu/azure/cosmosdb/CosmosStoreTest.java +++ b/src/test/java/org/opengroup/osdu/azure/cosmosdb/CosmosStoreTest.java @@ -21,7 +21,6 @@ import com.azure.cosmos.CosmosDatabase; import com.azure.cosmos.CosmosException; import com.azure.cosmos.implementation.ConflictException; import com.azure.cosmos.implementation.NotFoundException; - import com.azure.cosmos.models.CosmosItemRequestOptions; import com.azure.cosmos.models.CosmosItemResponse; import com.azure.cosmos.models.PartitionKey; @@ -36,6 +35,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.opengroup.osdu.azure.logging.CoreLogger; import org.opengroup.osdu.azure.logging.CoreLoggerFactory; +import org.opengroup.osdu.azure.logging.DependencyLogger; import org.opengroup.osdu.azure.multitenancy.TenantInfoDoc; import org.opengroup.osdu.core.common.model.http.AppException; @@ -48,7 +48,15 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.opengroup.osdu.azure.logging.DependencyType.COSMOS_STORE; @ExtendWith(MockitoExtension.class) class CosmosStoreTest { @@ -93,6 +101,9 @@ class CosmosStoreTest { @Mock private CosmosDatabase cosmosDatabase; + @Mock + private DependencyLogger dependencyLogger; + @InjectMocks private CosmosStore cosmosStore; @@ -154,6 +165,7 @@ class CosmosStoreTest { cosmosStore.deleteItem(DATA_PARTITION_ID, COSMOS_DB, COLLECTION, ID, PARTITION_KEY); }); assertEquals(404, exception.getError().getCode()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("DELETE_ITEM"), eq("id=id partition_key=pk"), eq("cosmosdb/collection"), anyLong(), eq(404), eq(false)); } @Test @@ -163,6 +175,7 @@ class CosmosStoreTest { cosmosStore.deleteItem(COSMOS_DB, COLLECTION, ID, PARTITION_KEY); }); assertEquals(404, exception.getError().getCode()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("DELETE_ITEM"), eq("id=id partition_key=pk"), eq("cosmosdb/collection"), anyLong(), eq(404), eq(false)); } @@ -173,6 +186,7 @@ class CosmosStoreTest { cosmosStore.deleteItem(DATA_PARTITION_ID, COSMOS_DB, COLLECTION, ID, PARTITION_KEY); }); assertEquals(500, exception.getError().getCode()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("DELETE_ITEM"), eq("id=id partition_key=pk"), eq("cosmosdb/collection"), anyLong(), eq(0), eq(false)); } @Test @@ -182,12 +196,14 @@ class CosmosStoreTest { cosmosStore.deleteItem(COSMOS_DB, COLLECTION, ID, PARTITION_KEY); }); assertEquals(500, exception.getError().getCode()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("DELETE_ITEM"), eq("id=id partition_key=pk"), eq("cosmosdb/collection"), anyLong(), eq(0), eq(false)); } @Test void findItem_returnsEmpty_ifNotFound() throws CosmosException { doThrow(NotFoundException.class).when(container).readItem(any(), any(), any(), any()); assertFalse(cosmosStore.findItem(DATA_PARTITION_ID, COSMOS_DB, COLLECTION, ID, PARTITION_KEY, any(Class.class)).isPresent()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("READ_ITEM"), eq("id=id partition_key=pk"), eq("cosmosdb/collection"), anyLong(), eq(404), eq(false)); } @Test @@ -195,6 +211,7 @@ class CosmosStoreTest { doThrow(NotFoundException.class).when(container).readItem(any(), any(), any(), any()); assertFalse(cosmosStore.findItem(COSMOS_DB, COLLECTION, ID, PARTITION_KEY, Object.class).isPresent()); verify(this.cosmosClientFactory, times(1)).getSystemClient(); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("READ_ITEM"), eq("id=id partition_key=pk"), eq("cosmosdb/collection"), anyLong(), eq(404), eq(false)); } @Test @@ -204,6 +221,7 @@ class CosmosStoreTest { cosmosStore.findItem(DATA_PARTITION_ID, COSMOS_DB, COLLECTION, ID, PARTITION_KEY, any(Class.class)); }); assertEquals(500, exception.getError().getCode()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("READ_ITEM"), eq("id=id partition_key=pk"), eq("cosmosdb/collection"), anyLong(), eq(0), eq(false)); } @Test @@ -222,6 +240,7 @@ class CosmosStoreTest { cosmosStore.upsertItem(DATA_PARTITION_ID, COSMOS_DB, COLLECTION, "some-data", any()); }); assertEquals(500, exception.getError().getCode()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("UPSERT_ITEM"), eq("partition_key=some-data"), eq("cosmosdb/collection"), anyLong(), eq(0), eq(false)); } @Test @@ -234,6 +253,7 @@ class CosmosStoreTest { assertEquals(500, exception.getError().getCode()); verify(container).replaceItem(eq(ITEM), eq(ID), any(PartitionKey.class), any(CosmosItemRequestOptions.class)); Assertions.assertTrue(partitionKeyArgumentCaptor.getValue().toString().contains(PARTITION_KEY)); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("REPLACE_ITEM"), eq("id=id partition_key=pk"), eq("data-partition-id:cosmosdb/collection"), anyLong(), eq(0), eq(false)); } @Test @@ -246,6 +266,7 @@ class CosmosStoreTest { assertEquals(404, exception.getError().getCode()); verify(container).replaceItem(eq(ITEM), eq(ID), any(PartitionKey.class), any(CosmosItemRequestOptions.class)); Assertions.assertTrue(partitionKeyArgumentCaptor.getValue().toString().contains(PARTITION_KEY)); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("REPLACE_ITEM"), eq("id=id partition_key=pk"), eq("data-partition-id:cosmosdb/collection"), anyLong(), eq(0), eq(false)); } @Test @@ -256,6 +277,7 @@ class CosmosStoreTest { cosmosStore.replaceItem(DATA_PARTITION_ID, COSMOS_DB, COLLECTION, ID, PARTITION_KEY, ITEM); verify(container).replaceItem(eq(ITEM), eq(ID), any(PartitionKey.class), any(CosmosItemRequestOptions.class)); Assertions.assertTrue(partitionKeyArgumentCaptor.getValue().toString().contains(PARTITION_KEY)); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("REPLACE_ITEM"), eq("id=id partition_key=pk"), eq("data-partition-id:cosmosdb/collection"), anyLong(), eq(200), eq(true)); } @Test @@ -265,6 +287,7 @@ class CosmosStoreTest { cosmosStore.createItem(DATA_PARTITION_ID, COSMOS_DB, COLLECTION, "some-data", any()); }); assertEquals(409, exception.getError().getCode()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("CREATE_ITEM"), eq("partition_key=some-data"), eq("cosmosdb/collection"), anyLong(), eq(0), eq(false)); } @Test @@ -274,6 +297,7 @@ class CosmosStoreTest { cosmosStore.createItem(COSMOS_DB, COLLECTION, "some-data", Object.class); }); assertEquals(409, exception.getError().getCode()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("CREATE_ITEM"), eq("partition_key=some-data"), eq("cosmosdb/collection"), anyLong(), eq(0), eq(false)); } @Test @@ -283,6 +307,7 @@ class CosmosStoreTest { cosmosStore.createItem(DATA_PARTITION_ID, COSMOS_DB, COLLECTION, "some-data", any()); }); assertEquals(500, exception.getError().getCode()); + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("CREATE_ITEM"), eq("partition_key=some-data"), eq("cosmosdb/collection"), anyLong(), eq(0), eq(false)); } @Test @@ -292,6 +317,7 @@ class CosmosStoreTest { } catch (Exception ex) { fail("Should not fail."); } + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("CREATE_ITEM"), eq("partition_key=some-data"), eq("cosmosdb/collection"), anyLong(), eq(200), eq(true)); } @Test @@ -301,6 +327,7 @@ class CosmosStoreTest { } catch (Exception ex) { fail("Should not fail."); } + verify(dependencyLogger, times(1)).logDependency(eq(COSMOS_STORE), eq("CREATE_ITEM"), eq("partition_key=some-data"), eq("cosmosdb/collection"), anyLong(), eq(200), eq(true)); } /*