diff --git a/CHANGELOG.md b/CHANGELOG.md index a655af41a..39aa8d3f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog ## [Unreleased] - TBD +### Added +* Fixed `B2StorageClient.deleteAllFilesInBucket` so it uses `fileVersions` instead of `fileNames`. +* Added `maxEventsPerBatch` field to `B2WebhookConfiguration`. + +### Changed +* Validate idle connections after 2 seconds of inactivity ## [6.2.1] - 2024-07-16 ### Added @@ -62,7 +68,7 @@ * Fixed B2ListFilesIterableBase assuming a response with 0 results was the end. It now looks for `nextFileName` being null to indicate the end. -### [6.1.0] - 2022-09-19 +## [6.1.0] - 2022-09-19 ### Added * Added support for custom upload timestamps * Added support for Java 8's `-parameters` option so constructor parameters do not need to be reiterated in `B2Json.constructor#params` diff --git a/core/src/main/java/com/backblaze/b2/client/B2StorageClient.java b/core/src/main/java/com/backblaze/b2/client/B2StorageClient.java index c68b1d044..8ee397814 100644 --- a/core/src/main/java/com/backblaze/b2/client/B2StorageClient.java +++ b/core/src/main/java/com/backblaze/b2/client/B2StorageClient.java @@ -775,7 +775,7 @@ default void deleteFileVersion(String fileName, * file versions have been deleted (if any) and which haven't (if any). */ default void deleteAllFilesInBucket(String bucketId) throws B2Exception { - for (B2FileVersion fileVersion: fileNames(bucketId)) { + for (B2FileVersion fileVersion: fileVersions(bucketId)) { deleteFileVersion(fileVersion); } } diff --git a/core/src/main/java/com/backblaze/b2/client/structures/B2WebhookConfiguration.java b/core/src/main/java/com/backblaze/b2/client/structures/B2WebhookConfiguration.java index e837e78aa..55d862849 100644 --- a/core/src/main/java/com/backblaze/b2/client/structures/B2WebhookConfiguration.java +++ b/core/src/main/java/com/backblaze/b2/client/structures/B2WebhookConfiguration.java @@ -27,30 +27,42 @@ public class B2WebhookConfiguration extends B2EventNotificationTargetConfigurati @B2Json.optional private final String hmacSha256SigningSecret; + /** + * An optional maximum number of events to batch into a single webhook request. + */ + @B2Json.optional(omitNull = true) + private final Integer maxEventsPerBatch; + @B2Json.constructor public B2WebhookConfiguration(String url, TreeSet customHeaders, - String hmacSha256SigningSecret) { + String hmacSha256SigningSecret, + Integer maxEventsPerBatch) { B2Preconditions.checkArgument( url != null && url.startsWith("https://"), "The protocol for the url must be https://" ); + B2Preconditions.checkArgument( + maxEventsPerBatch == null || (maxEventsPerBatch > 0 && maxEventsPerBatch <= 50), + "The events per batch must be between 1 and 50" + ); this.url = url; this.customHeaders = customHeaders; this.hmacSha256SigningSecret = hmacSha256SigningSecret; + this.maxEventsPerBatch = maxEventsPerBatch; } public B2WebhookConfiguration(String url) { - this(url, null, null); + this(url, null, null, null); } public B2WebhookConfiguration(String url, TreeSet customHeaders) { - this(url, customHeaders, null); + this(url, customHeaders, null, null); } public B2WebhookConfiguration(String url, String hmacSha256SigningSecret) { - this(url, null, hmacSha256SigningSecret); + this(url, null, hmacSha256SigningSecret, null); } public String getUrl() { @@ -68,6 +80,10 @@ public String getHmacSha256SigningSecret() { return hmacSha256SigningSecret; } + public Integer getMaxEventsPerBatch() { + return maxEventsPerBatch; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -75,7 +91,8 @@ public boolean equals(Object o) { final B2WebhookConfiguration that = (B2WebhookConfiguration) o; return url.equals(that.url) && Objects.equals(customHeaders, that.customHeaders) && - Objects.equals(hmacSha256SigningSecret, that.hmacSha256SigningSecret); + Objects.equals(hmacSha256SigningSecret, that.hmacSha256SigningSecret) && + Objects.equals(maxEventsPerBatch, that.maxEventsPerBatch); } @Override diff --git a/core/src/test/java/com/backblaze/b2/client/B2StorageClientImplTest.java b/core/src/test/java/com/backblaze/b2/client/B2StorageClientImplTest.java index 879ab185c..9326b6d67 100644 --- a/core/src/test/java/com/backblaze/b2/client/B2StorageClientImplTest.java +++ b/core/src/test/java/com/backblaze/b2/client/B2StorageClientImplTest.java @@ -521,6 +521,24 @@ public void testDeleteFileVersion() throws B2Exception { } + @Test + public void testDeleteAllFilesInBucket() throws B2Exception { + final B2ListFileVersionsRequest request = B2ListFileVersionsRequest.builder(bucketId(1)).setMaxFileCount(1000).build(); + final List versions = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + versions.add(makeVersion(i, i)); + } + final B2ListFileVersionsResponse response = new B2ListFileVersionsResponse(versions, null, null); + when(webifier.listFileVersions(anyObject(), eq(request))).thenReturn(response); + + client.deleteAllFilesInBucket(bucketId(1)); + + for (B2FileVersion version : versions) { + final B2DeleteFileVersionRequest deleteRequest = B2DeleteFileVersionRequest.builder(version.getFileName(), version.getFileId()).build(); + verify(webifier, times(1)).deleteFileVersion(anyObject(), eq(deleteRequest)); + } + } + @Test public void testGetDownloadAuthorization() throws B2Exception { final B2DownloadAuthorization downloadAuth = new B2DownloadAuthorization(bucketId(1), FILE_PREFIX, "downloadAuthToken"); diff --git a/core/src/test/java/com/backblaze/b2/client/B2StorageClientWebifierImplTest.java b/core/src/test/java/com/backblaze/b2/client/B2StorageClientWebifierImplTest.java index 66608fafb..4475de8bf 100644 --- a/core/src/test/java/com/backblaze/b2/client/B2StorageClientWebifierImplTest.java +++ b/core/src/test/java/com/backblaze/b2/client/B2StorageClientWebifierImplTest.java @@ -1975,7 +1975,8 @@ public void testSetBucketNotificationRules() throws B2Exception { new B2WebhookCustomHeader("name2", "val2") ) ), - "3XDfkdQte2OgA78qCtSD17LAzpj6ay9H" + "3XDfkdQte2OgA78qCtSD17LAzpj6ay9H", + 20 ), true ), @@ -2030,6 +2031,7 @@ public void testSetBucketNotificationRules() throws B2Exception { " }\n" + " ],\n" + " \"hmacSha256SigningSecret\": \"3XDfkdQte2OgA78qCtSD17LAzpj6ay9H\",\n" + + " \"maxEventsPerBatch\": 20,\n" + " \"targetType\": \"webhook\",\n" + " \"url\": \"https://www.example.com\"\n" + " }\n" + diff --git a/core/src/test/java/com/backblaze/b2/client/structures/B2EventNotificationRuleTest.java b/core/src/test/java/com/backblaze/b2/client/structures/B2EventNotificationRuleTest.java index 3642c54ca..d0601bc13 100644 --- a/core/src/test/java/com/backblaze/b2/client/structures/B2EventNotificationRuleTest.java +++ b/core/src/test/java/com/backblaze/b2/client/structures/B2EventNotificationRuleTest.java @@ -40,6 +40,7 @@ public void testToJsonAndBack() { " }\n" + " ],\n" + " \"hmacSha256SigningSecret\": \"3XDfkdQte2OgA78qCtSD17LAzpj6ay9H\",\n" + + " \"maxEventsPerBatch\": 20,\n" + " \"targetType\": \"webhook\",\n" + " \"url\": \"https://www.example.com\"\n" + " }\n" + @@ -66,7 +67,8 @@ public void testToJsonAndBack() { new B2WebhookCustomHeader("name2", "val2") ) ), - "3XDfkdQte2OgA78qCtSD17LAzpj6ay9H" + "3XDfkdQte2OgA78qCtSD17LAzpj6ay9H", + 20 ), true, false, diff --git a/core/src/test/java/com/backblaze/b2/client/structures/B2GetBucketNotificationRulesResponseTest.java b/core/src/test/java/com/backblaze/b2/client/structures/B2GetBucketNotificationRulesResponseTest.java index 113a4901d..866178173 100644 --- a/core/src/test/java/com/backblaze/b2/client/structures/B2GetBucketNotificationRulesResponseTest.java +++ b/core/src/test/java/com/backblaze/b2/client/structures/B2GetBucketNotificationRulesResponseTest.java @@ -35,7 +35,8 @@ public void testFullGetBucketNotificationRulesResponse() { new B2WebhookCustomHeader("name2", "val2") ) ), - "dummySigningSecret"), + "dummySigningSecret", + 20), true, false, null @@ -73,6 +74,7 @@ public void testFullGetBucketNotificationRulesResponse() { " }\n" + " ],\n" + " \"hmacSha256SigningSecret\": \"dummySigningSecret\",\n" + + " \"maxEventsPerBatch\": 20,\n" + " \"targetType\": \"webhook\",\n" + " \"url\": \"https://www.example.com\"\n" + " }\n" + diff --git a/core/src/test/java/com/backblaze/b2/client/structures/B2SetBucketNotificationRulesRequestTest.java b/core/src/test/java/com/backblaze/b2/client/structures/B2SetBucketNotificationRulesRequestTest.java index f69e79743..a7f943ba8 100644 --- a/core/src/test/java/com/backblaze/b2/client/structures/B2SetBucketNotificationRulesRequestTest.java +++ b/core/src/test/java/com/backblaze/b2/client/structures/B2SetBucketNotificationRulesRequestTest.java @@ -42,7 +42,8 @@ public void testFullSetBucketNotificationRulesRequest() { new B2WebhookCustomHeader("name2", "val2") ) ), - "rrzaVL6BqYt83s2Q5R2I79AilaxVBJUS" + "rrzaVL6BqYt83s2Q5R2I79AilaxVBJUS", + 20 ), true ) @@ -95,6 +96,7 @@ public void testFullSetBucketNotificationRulesRequest() { " }\n" + " ],\n" + " \"hmacSha256SigningSecret\": \"rrzaVL6BqYt83s2Q5R2I79AilaxVBJUS\",\n" + + " \"maxEventsPerBatch\": 20,\n" + " \"targetType\": \"webhook\",\n" + " \"url\": \"https://www.example.com\"\n" + " }\n" + diff --git a/core/src/test/java/com/backblaze/b2/client/structures/B2SetBucketNotificationRulesResponseTest.java b/core/src/test/java/com/backblaze/b2/client/structures/B2SetBucketNotificationRulesResponseTest.java index c9aede1c3..177c1a737 100644 --- a/core/src/test/java/com/backblaze/b2/client/structures/B2SetBucketNotificationRulesResponseTest.java +++ b/core/src/test/java/com/backblaze/b2/client/structures/B2SetBucketNotificationRulesResponseTest.java @@ -44,7 +44,8 @@ public void testFullSetBucketNotificationRulesResponse() { new B2WebhookCustomHeader("name2", "val2") ) ), - "dummySigningSecret"), + "dummySigningSecret", + 20), true, false, null @@ -98,6 +99,7 @@ public void testFullSetBucketNotificationRulesResponse() { " }\n" + " ],\n" + " \"hmacSha256SigningSecret\": \"dummySigningSecret\",\n" + + " \"maxEventsPerBatch\": 20,\n" + " \"targetType\": \"webhook\",\n" + " \"url\": \"https://www.example.com\"\n" + " }\n" + diff --git a/core/src/test/java/com/backblaze/b2/client/structures/B2WebhookConfigurationTest.java b/core/src/test/java/com/backblaze/b2/client/structures/B2WebhookConfigurationTest.java index e62bfab26..f59ed31b5 100644 --- a/core/src/test/java/com/backblaze/b2/client/structures/B2WebhookConfigurationTest.java +++ b/core/src/test/java/com/backblaze/b2/client/structures/B2WebhookConfigurationTest.java @@ -30,6 +30,7 @@ public void testUrlWithIncorrectProtocolThrows() { new B2WebhookCustomHeader("name2", "val2") ) ), + null, null ); fail("should have thrown"); @@ -39,6 +40,63 @@ public void testUrlWithIncorrectProtocolThrows() { } } + @Test + public void testUrlWithHighMaxEventsPerBatchThrows() { + try { + new B2WebhookConfiguration( + "https://www.backblaze.com", + new TreeSet<>( + listOf( + new B2WebhookCustomHeader("name1", "val1"), + new B2WebhookCustomHeader("name2", "val2") + ) + ), + null, + 500 + ); + fail("should have thrown"); + } + catch (IllegalArgumentException e) { + assertEquals("The events per batch must be between 1 and 50", e.getMessage()); + } + } + + @Test + public void testUrlWith50EventsPerBatchSucceeds() { + new B2WebhookConfiguration( + "https://www.backblaze.com", + new TreeSet<>( + listOf( + new B2WebhookCustomHeader("name1", "val1"), + new B2WebhookCustomHeader("name2", "val2") + ) + ), + null, + 50 + ); + } + + @Test + public void testUrlWithZeroMaxEventsPerBatchThrows() { + try { + new B2WebhookConfiguration( + "https://www.backblaze.com", + new TreeSet<>( + listOf( + new B2WebhookCustomHeader("name1", "val1"), + new B2WebhookCustomHeader("name2", "val2") + ) + ), + null, + 0 + ); + fail("should have thrown"); + } + catch (IllegalArgumentException e) { + assertEquals("The events per batch must be between 1 and 50", e.getMessage()); + } + } + @Test public void testToJsonAndBack() { final String jsonString = "{\n" + @@ -53,6 +111,7 @@ public void testToJsonAndBack() { " }\n" + " ],\n" + " \"hmacSha256SigningSecret\": \"rrzaVL6BqYt83s2Q5R2I79AilaxVBJUS\",\n" + + " \"maxEventsPerBatch\": 20,\n" + " \"targetType\": \"webhook\",\n" + " \"url\": \"https://www.example.com\"\n" + "}"; @@ -71,7 +130,8 @@ public void testToJsonAndBack() { new B2WebhookCustomHeader("name2", "val2") ) ), - "rrzaVL6BqYt83s2Q5R2I79AilaxVBJUS" + "rrzaVL6BqYt83s2Q5R2I79AilaxVBJUS", + 20 ); final String convertedJson = B2Json.toJsonOrThrowRuntime(defaultConfig); assertEquals(defaultConfig, converted); diff --git a/httpclient/src/main/java/com/backblaze/b2/client/webApiHttpClient/HttpClientFactoryImpl.java b/httpclient/src/main/java/com/backblaze/b2/client/webApiHttpClient/HttpClientFactoryImpl.java index 766f891d4..141305121 100644 --- a/httpclient/src/main/java/com/backblaze/b2/client/webApiHttpClient/HttpClientFactoryImpl.java +++ b/httpclient/src/main/java/com/backblaze/b2/client/webApiHttpClient/HttpClientFactoryImpl.java @@ -207,6 +207,7 @@ private HttpClientConnectionManager createConnectionManager() { final PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(registry); mgr.setMaxTotal(maxTotalConnectionsInPool); mgr.setDefaultMaxPerRoute(maxConnectionsPerRoute); + mgr.setValidateAfterInactivity(2000); return mgr; } }