From 25673fdc2622f19926e3634ae187182f64efec18 Mon Sep 17 00:00:00 2001 From: losalex <90795544+losalex@users.noreply.github.com> Date: Wed, 3 Nov 2021 11:09:25 -0700 Subject: [PATCH] feat: Add an ability to delete logs based on project, folder, organization or billing account resource names (#731) * fets: Add an ability to delete logs based on project, folder, organization or billing account resource names * Add an ability to make async and non-async delete calls in tests * Add proper validations with meaningful error message and also fix test name typo * Address PR comments * Move NPE validations into deleteLogAsync from getLogName --- .../clirr-ignored-differences.xml | 9 +++ .../com/google/cloud/logging/Logging.java | 57 ++++++++++++++++++ .../com/google/cloud/logging/LoggingImpl.java | 24 ++++++-- .../google/cloud/logging/LoggingImplTest.java | 58 +++++++++++++++++++ 4 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 google-cloud-logging/clirr-ignored-differences.xml diff --git a/google-cloud-logging/clirr-ignored-differences.xml b/google-cloud-logging/clirr-ignored-differences.xml new file mode 100644 index 000000000..49890ed46 --- /dev/null +++ b/google-cloud-logging/clirr-ignored-differences.xml @@ -0,0 +1,9 @@ + + + + + 7012 + com/google/cloud/logging/Logging + * deleteLog*(java.lang.String, com.google.cloud.logging.LogDestinationName) + + diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/Logging.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/Logging.java index 0e9bfb590..b665d96fd 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/Logging.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/Logging.java @@ -597,6 +597,63 @@ default ApiFuture> listLogsAsync(ListOption... options) { */ boolean deleteLog(String log); + /** + * Deletes a log and all its log entries for given log destination (see 'logName' parameter in + * https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry). The log will reappear if + * new entries are written to it. + * + *

Example of deleting a log by folder destination. + * + *

+   * {
+   *   @code
+   *   String logName = "my_log_name";
+   *   String folder = "my_folder";
+   *   boolean deleted = logging.deleteLog(logName, LogDestinationName.folder(folder));
+   *   if (deleted) {
+   *     // the log was deleted
+   *   } else {
+   *     // the log was not found
+   *   }
+   * }
+   * 
+ * + * @return {@code true} if the log was deleted, {@code false} if it was not found + */ + default boolean deleteLog(String log, LogDestinationName destination) { + throw new UnsupportedOperationException( + "method deleteLog() does not have default implementation"); + } + + /** + * Sends a request for deleting a log and all its log entries for given log destination (see + * 'logName' parameter in https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry). + * This method returns a {@code ApiFuture} object to consume the result. {@link ApiFuture#get()} + * returns {@code true} if the log was deleted, {@code false} if it was not found. + * + *

Example of asynchronously deleting a log by folder destination. + * + *

+   * {
+   *   @code
+   *   String logName = "my_log_name";
+   *   String folder = "my_folder";
+   *   ApiFuture future = logging.deleteLogAsync(logName, LogDestinationName.folder(folder));
+   *   // ...
+   *   boolean deleted = future.get();
+   *   if (deleted) {
+   *     // the log was deleted
+   *   } else {
+   *     // the log was not found
+   *   }
+   * }
+   * 
+ */ + default ApiFuture deleteLogAsync(String log, LogDestinationName destination) { + throw new UnsupportedOperationException( + "method deleteLogAsync() does not have default implementation"); + } + /** * Sends a request for deleting a log and all its log entries. This method returns a {@code * ApiFuture} object to consume the result. {@link ApiFuture#get()} returns {@code true} if the diff --git a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java index 40fe150ca..2f2d194bf 100644 --- a/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java +++ b/google-cloud-logging/src/main/java/com/google/cloud/logging/LoggingImpl.java @@ -40,6 +40,7 @@ import com.google.cloud.logging.spi.v2.LoggingRpc; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; +import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; @@ -440,14 +441,27 @@ public ApiFuture> listLogsAsync(ListOption... options) { } public boolean deleteLog(String log) { - return get(deleteLogAsync(log)); + return get(deleteLogAsync(log, null)); + } + + @Override + public boolean deleteLog(String log, LogDestinationName destination) { + return get(deleteLogAsync(log, destination)); } public ApiFuture deleteLogAsync(String log) { - DeleteLogRequest request = - DeleteLogRequest.newBuilder() - .setLogName(LogName.ofProjectLogName(getOptions().getProjectId(), log).toString()) - .build(); + return deleteLogAsync(log, null); + } + + @Override + public ApiFuture deleteLogAsync(String log, LogDestinationName destination) { + Preconditions.checkNotNull(log, "log parameter cannot be null"); + String projectId = getOptions().getProjectId(); + if (destination == null) { + Preconditions.checkNotNull(projectId, "projectId parameter cannot be null"); + } + LogName name = getLogName(projectId, log, destination); + DeleteLogRequest request = DeleteLogRequest.newBuilder().setLogName(name.toString()).build(); return transform(rpc.delete(request), EMPTY_TO_BOOLEAN_FUNCTION); } diff --git a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java index 4da20a090..919cdaa73 100644 --- a/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java +++ b/google-cloud-logging/src/test/java/com/google/cloud/logging/LoggingImplTest.java @@ -1777,6 +1777,50 @@ public void testDeleteLogAsync() throws ExecutionException, InterruptedException assertTrue(logging.deleteLogAsync(LOG_NAME).get()); } + @Test + public void testDeleteLogBillingDestination() throws ExecutionException, InterruptedException { + testDeleteByDestination( + LOG_NAME, LOG_NAME_BILLING_PATH, LogDestinationName.billingAccount(BILLING), false); + } + + @Test + public void testDeleteLogBillingDestinationAsync() + throws ExecutionException, InterruptedException { + testDeleteByDestination( + LOG_NAME, LOG_NAME_BILLING_PATH, LogDestinationName.billingAccount(BILLING), true); + } + + @Test + public void testDeleteLogFolderDestination() throws ExecutionException, InterruptedException { + testDeleteByDestination( + LOG_NAME, LOG_NAME_FOLDER_PATH, LogDestinationName.folder(FOLDER), false); + } + + @Test + public void testDeleteLogFolderDestinationAsync() + throws ExecutionException, InterruptedException { + testDeleteByDestination( + LOG_NAME, LOG_NAME_FOLDER_PATH, LogDestinationName.folder(FOLDER), true); + } + + @Test + public void testDeleteLogOrgDestination() throws ExecutionException, InterruptedException { + testDeleteByDestination( + LOG_NAME, LOG_NAME_ORGANIZATION_PATH, LogDestinationName.organization(ORGANIZATION), false); + } + + @Test + public void testDeleteLogOrgDestinationAsync() throws ExecutionException, InterruptedException { + testDeleteByDestination( + LOG_NAME, LOG_NAME_ORGANIZATION_PATH, LogDestinationName.organization(ORGANIZATION), true); + } + + @Test + public void testDeleteLogProjectDestination() throws ExecutionException, InterruptedException { + testDeleteByDestination( + LOG_NAME, LOG_NAME_PROJECT_PATH, LogDestinationName.project(PROJECT), false); + } + @Test public void testDeleteLogAsync_Null() throws ExecutionException, InterruptedException { DeleteLogRequest request = @@ -2241,6 +2285,20 @@ public void run() { assertSame(0, exceptions.get()); } + private void testDeleteByDestination( + String logId, String logName, LogDestinationName destination, boolean useAsyncDelete) + throws ExecutionException, InterruptedException { + DeleteLogRequest request = DeleteLogRequest.newBuilder().setLogName(logName).build(); + ApiFuture response = ApiFutures.immediateFuture(Empty.getDefaultInstance()); + EasyMock.expect(loggingRpcMock.delete(request)).andReturn(response); + EasyMock.replay(rpcFactoryMock, loggingRpcMock); + logging = options.getService(); + assertTrue( + useAsyncDelete + ? logging.deleteLogAsync(logId, destination).get() + : logging.deleteLog(logId, destination)); + } + private void testWriteLogEntriesWithDestination( String projectId, String fullLogNamePath, LogDestinationName destination) { Map labels = ImmutableMap.of("key", "value");