From 923594a12484bc86279d6d05f488093a1e12a499 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:04:20 +0200 Subject: [PATCH 01/40] Add restrictions field to metadata (#312) --- .../cloudinary/metadata/MetadataField.java | 9 +++++ .../com/cloudinary/metadata/Restrictions.java | 40 +++++++++++++++++++ .../test/AbstractStructuredMetadataTest.java | 24 +++++++++-- 3 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/Restrictions.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java index bae2b6c7..7dee301f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java @@ -16,6 +16,7 @@ public class MetadataField extends JSONObject { public static final String MANDATORY = "mandatory"; public static final String TYPE = "type"; public static final String VALIDATION = "validation"; + public static final String RESTRICTIONS = "restrictions"; public MetadataField(MetadataFieldType type) { put(TYPE, type.toString()); @@ -130,4 +131,12 @@ public MetadataDataSource getDataSource() { public void setDataSource(MetadataDataSource dataSource) { put("datasource", dataSource); } + + /** + * Set the restrictions rules of this field. + * @param restrictions The rules to set. + */ + public void setRestrictions(Restrictions restrictions) { + put(RESTRICTIONS, restrictions.toHash()); + } } \ No newline at end of file diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/Restrictions.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/Restrictions.java new file mode 100644 index 00000000..25d80d12 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/Restrictions.java @@ -0,0 +1,40 @@ +package com.cloudinary.metadata; + +import java.util.HashMap; + +/** + * Represents the restrictions metadata field. + */ +public class Restrictions { + + private final HashMap restrictions = new HashMap(); + + /** + * Set the custom field into restrictions. + * @param key The key of the field. + * @param value The value of the field. + */ + public Restrictions setRestriction(String key, Object value) { + restrictions.put(key, value); + return this; + } + + /** + * Set the read only ui field. + * @param value The read only ui value. + */ + public Restrictions setReadOnlyUI(Boolean value) { + return setRestriction("readonly_ui", value); + } + + /** + * Set the read only ui field to true. + */ + public Restrictions setReadOnlyUI() { + return this.setReadOnlyUI(true); + } + + public HashMap toHash() { + return restrictions; + } +} diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java index b6e541a7..bb8f1b9b 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java @@ -72,6 +72,18 @@ public void testCreateMetadata() throws Exception { assertEquals(setField.getLabel(), result.get("label")); } + @Test + public void testFieldRestrictions() throws Exception { + StringMetadataField stringField = newFieldInstance("testCreateMetadata_3"); + stringField.setRestrictions(new Restrictions().setReadOnlyUI()); + + ApiResponse result = api.addMetadataField(stringField); + assertNotNull(result); + Map restrictions = (Map) result.get("restrictions"); + assertNotNull(restrictions); + assertTrue((Boolean) restrictions.get("readonly_ui")); + } + @Test public void testDateFieldDefaultValueValidation() throws Exception { // now minus 3 days hours. @@ -130,13 +142,17 @@ public void testGetMetadata() throws Exception { @Test public void testUpdateField() throws Exception { - ApiResponse fieldResult = addFieldToAccount(newFieldInstance("testUpdateField")); + StringMetadataField metadataField = newFieldInstance("testUpdateField"); + ApiResponse fieldResult = addFieldToAccount(metadataField); assertNotEquals("new_def", fieldResult.get("default_value")); - StringMetadataField field = new StringMetadataField(); - field.setDefaultValue("new_def"); - ApiResponse result = api.updateMetadataField(fieldResult.get("external_id").toString(), field); + metadataField.setDefaultValue("new_def"); + metadataField.setRestrictions(new Restrictions().setReadOnlyUI()); + ApiResponse result = api.updateMetadataField(fieldResult.get("external_id").toString(), metadataField); assertNotNull(result); assertEquals("new_def", result.get("default_value")); + Map restrictions = (Map) result.get("restrictions"); + assertNotNull(restrictions); + assertTrue((Boolean)restrictions.get("readonly_ui")); } @Test From 09e0719c7750233890f0c75026dab10828363a4b Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 6 Mar 2024 14:17:28 +0200 Subject: [PATCH 02/40] Add access key management --- .../com/cloudinary/provisioning/Account.java | 56 +++++++++++++++++++ .../test/AbstractAccountApiTest.java | 41 ++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java index 55860aa5..c149e5dc 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java +++ b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java @@ -14,8 +14,10 @@ public class Account { private static final String CLOUDINARY_ACCOUNT_URL = "CLOUDINARY_ACCOUNT_URL"; public static final String PROVISIONING = "provisioning"; public static final String ACCOUNTS = "accounts"; + public static final String SUB_ACCOUNTS = "sub_accounts"; public static final String USERS = "users"; public static final String USER_GROUPS = "user_groups"; + public static final String ACCESS_KEYS = "access_keys"; private final AccountConfiguration configuration; private final String accountId; @@ -619,6 +621,60 @@ public ApiResponse userGroupUsers(String groupId, Map options) t return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); } + /** + * Lists the access keys belonging to this sub account id. + * @param subAccountId The id of the user group. + * @param options Generic advanced options map, see online documentation. + * @return The list of access keys in that sub account id. + * @throws Exception If the request fails. + */ + public ApiResponse getAccessKeys(String subAccountId, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, SUB_ACCOUNTS, subAccountId); + return callAccountApi(Api.HttpMethod.GET, uri, Collections.emptyMap(), options); + } + + /** + * Creates a new access key for this sub account id. + * @param subAccountId The id of the user group. + * @param name The name for the access key. + * @param enabled Access key's status (enabled or disabled). + * @param options Generic advanced options map, see online documentation. + * @return The created access key. + * @throws Exception If the request fails. + */ + public ApiResponse createAccessKey(String subAccountId, String name, Boolean enabled, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, SUB_ACCOUNTS, subAccountId, ACCESS_KEYS); + return callAccountApi(Api.HttpMethod.POST, uri, ObjectUtils.asMap("name", name, "enabled", enabled), options); + } + + /** + * Updates an existing access key for this sub account id. + * @param subAccountId The id of the user group. + * @param accessKey The key of the access key. + * @param name The name for the access key. + * @param enabled Access key's status (enabled or disabled). + * @param options Generic advanced options map, see online documentation. + * @return The updated access key. + * @throws Exception If the request fails. + */ + public ApiResponse updateAccessKey(String subAccountId, String accessKey, String name, Boolean enabled, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, SUB_ACCOUNTS, subAccountId, ACCESS_KEYS, accessKey); + return callAccountApi(Api.HttpMethod.PUT, uri, ObjectUtils.asMap("name", name, "enabled", enabled), options); + } + + /** + * Deletes an existing access key for this sub account id. + * @param subAccountId The id of the user group. + * @param accessKey The key of the access key. + * @param options Generic advanced options map, see online documentation. + * @return "message": "ok". + * @throws Exception If the request fails. + */ + public ApiResponse deleteAccessKey(String subAccountId, String accessKey, Map options) throws Exception { + List uri = Arrays.asList(PROVISIONING, ACCOUNTS, accountId, SUB_ACCOUNTS, subAccountId, ACCESS_KEYS, accessKey); + return callAccountApi(Api.HttpMethod.DELETE, uri, Collections.emptyMap(), options); + } + /** * Private helper method for users api calls * @param method Http method diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java index 7513907a..5b8e9633 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java @@ -423,6 +423,47 @@ public void testListUsersInGroup() throws Exception { deleteUser(user2Id); } + @Test + public void testGetAccessKeys() throws Exception { + ApiResponse createResult = createSubAccount(); + ApiResponse result = account.getAccessKeys((String) createResult.get("id"), ObjectUtils.emptyMap()); + assertNotNull(result); + } + + @Test + public void testCreateNewAccessKey() throws Exception { + ApiResponse createResult = createSubAccount(); + String name = randomLetters(); + ApiResponse result = account.createAccessKey((String)createResult.get("id"), name, true, ObjectUtils.emptyMap()); + assertNotNull(result); + assertTrue((Boolean) result.get("enabled")); + } + + @Test + public void testUpdateAccessKey() throws Exception { + ApiResponse createResult = createSubAccount(); + String name = randomLetters(); + ApiResponse result = account.createAccessKey((String)createResult.get("id"), name, false, ObjectUtils.emptyMap()); + assertNotNull(result); + + String updatedName = randomLetters(); + result = account.updateAccessKey((String)createResult.get("id"), (String) result.get("api_key"), updatedName, true, ObjectUtils.emptyMap()); + assertNotNull(result); + assertEquals(updatedName, result.get("name")); + assertTrue((Boolean) result.get("enabled")); + } + + @Test + public void testDeleteAccessKey() throws Exception { + ApiResponse createResult = createSubAccount(); + String name = randomLetters(); + ApiResponse result = account.createAccessKey((String)createResult.get("id"), name, false, ObjectUtils.emptyMap()); + assertNotNull(result); + + result = account.deleteAccessKey((String)createResult.get("id"), (String) result.get("api_key"), ObjectUtils.emptyMap()); + assertNotNull(result); + } + // Helpers private ApiResponse createGroup() throws Exception { From 959f98acd45da23f941c88f04a600271166ce1db Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:08:16 +0200 Subject: [PATCH 03/40] Add selective response support --- .../src/main/java/com/cloudinary/Api.java | 33 ++++++++++++++----- .../src/main/java/com/cloudinary/Search.java | 12 +++++++ .../com/cloudinary/test/AbstractApiTest.java | 27 +++++++++++++++ .../cloudinary/test/AbstractSearchTest.java | 12 +++++++ 4 files changed, 75 insertions(+), 9 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 828b821b..24df4a05 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -87,8 +87,10 @@ public ApiResponse resources(Map options) throws Exception { uri.add(resourceType); if (type != null) uri.add(type); - - ApiResponse response = callApi(HttpMethod.GET, uri, ObjectUtils.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at", "metadata"), options); + if(options.get("fields") != null) { + options.put("fields", StringUtils.join(ObjectUtils.asArray(options.get("fields")), ",")); + } + ApiResponse response = callApi(HttpMethod.GET, uri, ObjectUtils.only(options, "next_cursor", "direction", "max_results", "prefix", "tags", "context", "moderations", "start_at", "metadata", "fields"), options); return response; } @@ -106,8 +108,10 @@ public ApiResponse visualSearch(Map options) throws Exception { public ApiResponse resourcesByTag(String tag, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); - - ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations", "metadata"), options); + if(options.get("fields") != null) { + options.put("fields", StringUtils.join(ObjectUtils.asArray(options.get("fields")), ",")); + } + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "tags", tag), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations", "metadata", "fields"), options); return response; } @@ -118,7 +122,10 @@ public ApiResponse resourcesByContext(String key, Map options) throws Exception public ApiResponse resourcesByContext(String key, String value, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); - Map params = ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations", "metadata"); + if(options.get("fields") != null) { + options.put("fields", StringUtils.join(ObjectUtils.asArray(options.get("fields")), ",")); + } + Map params = ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations", "metadata", "fields"); params.put("key", key); if (StringUtils.isNotBlank(value)) { params.put("value", value); @@ -128,7 +135,10 @@ public ApiResponse resourcesByContext(String key, String value, Map options) thr public ApiResponse resourceByAssetID(String assetId, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - Map params = ObjectUtils.only(options, "tags", "context", "moderations"); + if(options.get("fields") != null) { + options.put("fields", StringUtils.join(ObjectUtils.asArray(options.get("fields")), ",")); + } + Map params = ObjectUtils.only(options, "tags", "context", "moderations", "fields"); ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", assetId), params, options); return response; } @@ -142,7 +152,10 @@ public ApiResponse resourcesByAssetIDs(Iterable assetIds, Map options) t public ApiResponse resourcesByAssetFolder(String assetFolder, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - Map params = ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations"); + if(options.get("fields") != null) { + options.put("fields", StringUtils.join(ObjectUtils.asArray(options.get("fields")), ",")); + } + Map params = ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations", "fields"); params.put("asset_folder", assetFolder); ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources/by_asset_folder"), params, options); return response; @@ -161,8 +174,10 @@ public ApiResponse resourcesByIds(Iterable publicIds, Map options) throw public ApiResponse resourcesByModeration(String kind, String status, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); - - ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "moderations", kind, status), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations", "metadata"), options); + if(options.get("fields") != null) { + options.put("fields", StringUtils.join(ObjectUtils.asArray(options.get("fields")), ",")); + } + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, "moderations", kind, status), ObjectUtils.only(options, "next_cursor", "direction", "max_results", "tags", "context", "moderations", "metadata", "fields"), options); return response; } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Search.java b/cloudinary-core/src/main/java/com/cloudinary/Search.java index 652e2cda..2ba3ac8b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Search.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Search.java @@ -19,6 +19,7 @@ public class Search { private ArrayList aggregateParam; private ArrayList withFieldParam; private HashMap params; + private ArrayList fields; private int ttl = 300; @@ -28,6 +29,7 @@ public class Search { this.sortByParam = new ArrayList>(); this.aggregateParam = new ArrayList(); this.withFieldParam = new ArrayList(); + this.fields = new ArrayList(); } public Search ttl(int ttl) { @@ -76,6 +78,13 @@ public Search sortBy(String field, String dir) { return this; } + public Search fields(String field) { + if (!fields.contains(field)) { + fields.add(field); + } + return this; + } + public HashMap toQuery() { HashMap queryParams = new HashMap(this.params); if (withFieldParam.size() > 0) { @@ -87,6 +96,9 @@ public HashMap toQuery() { if(aggregateParam.size() > 0) { queryParams.put("aggregate", aggregateParam); } + if(fields.size() > 0) { + queryParams.put("fields", fields); + } return queryParams; } diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index 24cc490d..141b1ff3 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -181,6 +181,33 @@ public void test01ResourceTypes() throws Exception { assertThat(resource_types, hasItem("image")); } + @Test + public void testSingleSelectiveResponse() throws Exception { + Map options = new HashMap(); + options.put("fields", "width"); + Map result = api.resources(options); + List resources = (List) result.get("resources"); + assertNotNull(resources); + Map resource = resources.get(0); + assertNotNull(resource); + assertNotNull(resource.get("width")); + assertNull(resource.get("format")); + } + + @Test + public void testMultipleSelectiveResponse() throws Exception { + Map options = new HashMap(); + options.put("fields", new String[]{"width", "format"}); + Map result = api.resources(options); + List resources = (List) result.get("resources"); + assertNotNull(resources); + Map resource = resources.get(0); + assertNotNull(resource); + assertNotNull(resource.get("width")); + assertNotNull(resource.get("format")); + assertNull(resource.get("height")); + } + @Test public void test03ResourcesCursor() throws Exception { // should allow listing resources with cursor diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java index 1d0a2094..5e30d26b 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java @@ -177,4 +177,16 @@ public void testShouldBuildSearchUrl() throws Exception { cloudinaryToSearch.config.privateCdn = true; assertEquals(String.format("https://%s-res.cloudinary.com/search/%s/%d/%s", cloudinaryToSearch.config.cloudName, ttl300Signature, 300, base64Query), search.toUrl(300, "")); } + + @Test + public void testSearchWithSelectiveResponse() throws Exception { + Map result = cloudinary.search().expression(String.format("tags:%s", SEARCH_TAG)).fields("width").fields("height").execute(); + List resources = (List) result.get("resources"); + assertEquals(3, resources.size()); + Map resource = resources.get(0); + assertNotNull(resource); + assertNotNull(resource.get("width")); + assertNotNull(resource.get("height")); + assertNull(resource.get("format")); + } } \ No newline at end of file From 36d721803f6332b07a35cb12c355f05f85435cef Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:37:22 +0200 Subject: [PATCH 04/40] Add analyze api --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 11 +++++++++++ .../cloudinary/strategies/AbstractApiStrategy.java | 10 +++++++--- .../main/java/com/cloudinary/http42/ApiStrategy.java | 5 +---- .../main/java/com/cloudinary/http43/ApiStrategy.java | 5 +---- .../main/java/com/cloudinary/http44/ApiStrategy.java | 5 +---- .../main/java/com/cloudinary/http45/ApiStrategy.java | 6 +----- .../java/com/cloudinary/test/AbstractApiTest.java | 8 ++++++++ 7 files changed, 30 insertions(+), 20 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 24df4a05..f97ed09e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -779,6 +779,17 @@ public ApiResponse reorderMetadataFields(String orderBy, String direction, Map o return callApi(HttpMethod.PUT, uri, map, options); } + public ApiResponse analyze(String inputType, String analysisType, String uri, Map options) throws Exception { + if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); + List url = Arrays.asList("analysis", "analyze", inputType); + options.put("api_version", "v2"); + options.put("content_type", "json"); + final Map params = new HashMap(); + params.put("analysis_type", analysisType); + params.put("uri", uri); + return callApi(HttpMethod.POST, url, params, options); + } + private Map extractParams(Map options, List keys) { Map result = new HashMap(); for (String key : keys) { diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java index 9e427c4a..2d5a514d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -5,6 +5,7 @@ import com.cloudinary.SmartUrlEncoder; import com.cloudinary.api.ApiResponse; import com.cloudinary.utils.Base64Coder; +import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; import java.util.Arrays; import java.util.Map; @@ -17,9 +18,12 @@ public void init(Api api) { this.api = api; } - protected String createApiUrl (Iterable uri, String prefix, String cloudName){ - - String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1", cloudName), "/"); + protected String createApiUrl (Iterable uri, Map options){ + String version = ObjectUtils.asString(options.get("api_version"), "v1_1"); + String prefix = ObjectUtils.asString(options.get("upload_prefix"), ObjectUtils.asString(this.api.cloudinary.config.uploadPrefix, "https://api.cloudinary.com")); + String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.api.cloudinary.config.cloudName); + if (cloudName == null) throw new IllegalArgumentException("Must supply cloud_name"); + String apiUrl = StringUtils.join(Arrays.asList(prefix, version, cloudName), "/"); for (String component : uri) { component = SmartUrlEncoder.encode(component); apiUrl = apiUrl + "/" + component; diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java index 6703448e..4a90ed86 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java @@ -34,9 +34,6 @@ public class ApiStrategy extends AbstractApiStrategy { public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); - String prefix = ObjectUtils.asString(options.get("upload_prefix"), ObjectUtils.asString(this.api.cloudinary.config.uploadPrefix, "https://api.cloudinary.com")); - String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.api.cloudinary.config.cloudName); - if (cloudName == null) throw new IllegalArgumentException("Must supply cloud_name"); String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.config.apiKey); String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); String oauthToken = ObjectUtils.asString(options.get("oauth_token"), this.api.cloudinary.config.oauthToken); @@ -44,7 +41,7 @@ public ApiResponse callApi(HttpMethod method, Iterable uri, Map uri, Map uri, Map uri, Map Date: Thu, 28 Mar 2024 10:54:32 +0200 Subject: [PATCH 05/40] Add filename test to upload large --- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java index 1c12748b..bddf48cc 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java @@ -571,11 +571,12 @@ public void testUploadLarge() throws Exception { assertEquals("raw", resource.get("resource_type")); assertTrue(resource.get("public_id").toString().startsWith("cldupload")); - resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), asMap("chunk_size", 5243000, "tags", tags)); + resource = cloudinary.uploader().uploadLarge(new FileInputStream(temp), asMap("filename", "test123", "chunk_size", 5243000, "tags", tags)); assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); assertEquals("image", resource.get("resource_type")); assertEquals(1400, resource.get("width")); assertEquals(1400, resource.get("height")); + assertEquals("test123", resource.get("original_filename")); resource = cloudinary.uploader().uploadLarge(temp, asMap("chunk_size", 5880138, "tags", tags)); assertArrayEquals(tags, ((java.util.ArrayList) resource.get("tags")).toArray()); From 40941ecb3485c3fcd530293df20508e1fe229618 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 30 Apr 2024 07:19:28 +0300 Subject: [PATCH 06/40] Add rename folder api support --- .../src/main/java/com/cloudinary/Api.java | 12 ++++++++++++ .../java/com/cloudinary/test/AbstractApiTest.java | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index f97ed09e..c9b59d0e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -790,6 +790,18 @@ public ApiResponse analyze(String inputType, String analysisType, String uri, Ma return callApi(HttpMethod.POST, url, params, options); } + public ApiResponse renameFolder(String path, String toPath, Map options) throws Exception { + if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); + List url = Arrays.asList("folder_operations", "rename"); + + final Map params = new HashMap(); + params.put("path", path); + params.put("to_path", toPath); + + return callApi(HttpMethod.PUT, url, params, options); + + } + private Map extractParams(Map options, List keys) { Map result = new HashMap(); for (String key : keys) { diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index 15bbf17c..889b15f6 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -1267,4 +1267,15 @@ public void testVisualSearch() { Util.processWriteParameters(options, params); assertEquals(true, params.get("visual_search")); } + + @Test + @Ignore("Skip test till FD is enabled for test accounts") + public void testRenameFolder() throws Exception { + Map result = api.createFolder("apTestCreateFolder", null); + assertNotNull(result); + + String folderName = (String) result.get("path"); + Map response = api.renameFolder(folderName, "newFolderName", ObjectUtils.emptyMap()); + assertNotNull(response); + } } From bd829089b00690a694838fea30a99ab7cf55ae69 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Sun, 5 May 2024 08:20:15 +0300 Subject: [PATCH 07/40] Add delete backup asset version support --- .../src/main/java/com/cloudinary/Api.java | 19 ++++++++++++++++++ .../com/cloudinary/test/AbstractApiTest.java | 20 +++++++++++++++++++ .../com/cloudinary/test/helpers/Feature.java | 1 + 3 files changed, 40 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index c9b59d0e..daaa30df 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -802,6 +802,25 @@ public ApiResponse renameFolder(String path, String toPath, Map options) throws } + public ApiResponse deleteBackedUpAssets(String assetId, String[] versionIds, Map options) throws Exception { + if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); + if (StringUtils.isEmpty(assetId)) { + throw new IllegalArgumentException("AssetId parameter is required"); + } + + if (versionIds == null || versionIds.length == 0) { + throw new IllegalArgumentException("VersionIds parameter is required"); + } + + List url = Arrays.asList("resources", "backup", assetId); + + Map params = new HashMap(); + params.put("version_ids[]", StringUtils.join(versionIds, "&")); + + return callApi(HttpMethod.DELETE, url, params, options); + + } + private Map extractParams(Map options, List keys) { Map result = new HashMap(); for (String key : keys) { diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index 889b15f6..7ffd138e 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -1278,4 +1278,24 @@ public void testRenameFolder() throws Exception { Map response = api.renameFolder(folderName, "newFolderName", ObjectUtils.emptyMap()); assertNotNull(response); } + + @Test + public void testDeleteBackedupAsset() throws Exception { + if (MockableTest.shouldTestFeature(Feature.BACKEDUP_ASSETS)) { + Map result = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("backup", true)); + + String publicId = (String) result.get("public_id"); + String assetId = (String) result.get("asset_id"); + + ApiResponse getVersionsResp = api.resource(publicId, ObjectUtils.asMap("versions", true)); + List versions = (List) getVersionsResp.get("versions"); + String firstAssetVersion = (String) versions.get(0).get("version_id"); + ApiResponse response = api.deleteBackedUpAssets(assetId, new String[]{firstAssetVersion}, ObjectUtils.emptyMap()); + + assertNotNull(response); + assertEquals(response.get("asset_id"), assetId); + List deletedVersionIds = (List) response.get("deleted_version_ids"); + assertEquals(deletedVersionIds.get(0), firstAssetVersion); + } + } } diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java index 875f9b9f..aa4297c8 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java @@ -3,4 +3,5 @@ public class Feature { public static final String ALL = "all"; public static final String DYNAMIC_FOLDERS = "dynamic_folders"; + public static final String BACKEDUP_ASSETS = "backedup_assets"; } From 52d8f8feb029e4f5bedd403576c1e42a75f037dc Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 8 May 2024 11:34:25 +0300 Subject: [PATCH 08/40] Add config api call --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 10 ++++++++++ .../main/java/com/cloudinary/test/AbstractApiTest.java | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index daaa30df..a79409f5 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -72,6 +72,16 @@ public ApiResponse usage(Map options) throws Exception { return callApi(HttpMethod.GET, uri, ObjectUtils.emptyMap(), options); } + public ApiResponse configuration(Map options) throws Exception { + if(options == null) options = ObjectUtils.emptyMap(); + + final List uri = new ArrayList(); + uri.add("config"); + + Map params = ObjectUtils.only(options, "settings"); + + return callApi(HttpMethod.GET, uri, params, options); + } public ApiResponse resourceTypes(Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index 7ffd138e..26726340 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -668,6 +668,13 @@ public void testRateLimits() throws Exception { Assert.assertNotEquals(0, result.apiRateLimit().getRemaining()); } + @Test + public void testConfiguration() throws Exception { + ApiResponse result = cloudinary.api().configuration(new ObjectUtils().asMap("settings", true)); + Map settings = (Map) result.get("settings"); + Assert.assertNotNull(settings.get("folder_mode")); + } + @Test public void test19Ping() throws Exception { // should support ping API call From 94825e3de8f05848e9913e58fcca70ebd442ffa5 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 9 May 2024 11:47:40 +0300 Subject: [PATCH 09/40] Fix rename folder endpoint --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 5 ++--- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index a79409f5..9af8639b 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -802,11 +802,10 @@ public ApiResponse analyze(String inputType, String analysisType, String uri, Ma public ApiResponse renameFolder(String path, String toPath, Map options) throws Exception { if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); - List url = Arrays.asList("folder_operations", "rename"); + List url = Arrays.asList("folders", path); final Map params = new HashMap(); - params.put("path", path); - params.put("to_path", toPath); + params.put("to_folder", toPath); return callApi(HttpMethod.PUT, url, params, options); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index 26726340..cd1ccc88 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -1278,11 +1278,11 @@ public void testVisualSearch() { @Test @Ignore("Skip test till FD is enabled for test accounts") public void testRenameFolder() throws Exception { - Map result = api.createFolder("apTestCreateFolder", null); + Map result = api.createFolder("apiTestCreateFolder" + SUFFIX, null); assertNotNull(result); String folderName = (String) result.get("path"); - Map response = api.renameFolder(folderName, "newFolderName", ObjectUtils.emptyMap()); + Map response = api.renameFolder(folderName, "newFolderName" + SUFFIX, ObjectUtils.emptyMap()); assertNotNull(response); } From 922d69198908f84676be5882cab05868c27e686c Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:48:58 +0300 Subject: [PATCH 10/40] Add conditional metadata rules api --- .../src/main/java/com/cloudinary/Api.java | 30 +++++++++ .../com/cloudinary/metadata/MetadataRule.java | 66 +++++++++++++++++++ .../metadata/MetadataRuleCondition.java | 58 ++++++++++++++++ .../metadata/MetadataRuleResult.java | 58 ++++++++++++++++ .../test/AbstractStructuredMetadataTest.java | 52 ++++++++++++++- .../com/cloudinary/test/helpers/Feature.java | 1 + 6 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRule.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleCondition.java create mode 100644 cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleResult.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 9af8639b..2899327d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -7,6 +7,7 @@ import com.cloudinary.api.exceptions.*; import com.cloudinary.metadata.MetadataField; import com.cloudinary.metadata.MetadataDataSource; +import com.cloudinary.metadata.MetadataRule; import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; @@ -789,6 +790,35 @@ public ApiResponse reorderMetadataFields(String orderBy, String direction, Map o return callApi(HttpMethod.PUT, uri, map, options); } + public ApiResponse listMetadataRules(Map options) throws Exception { + if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); + final Map params = new HashMap(); + List uri = Arrays.asList("metadata_rules"); + return callApi(HttpMethod.GET, uri, params, options); + } + + public ApiResponse addMetadataRule(MetadataRule rule, Map options) throws Exception { + if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); + options.put("content_type", "json"); + final Map params = rule.asMap(); + List uri = Arrays.asList("metadata_rules"); + return callApi(HttpMethod.POST, uri, params, options); + } + + public ApiResponse updateMetadataRule(String externalId, MetadataRule rule, Map options) throws Exception { + if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); + options.put("content_type", "json"); + final Map params = rule.asMap(); + List uri = Arrays.asList("metadata_rules", externalId); + return callApi(HttpMethod.PUT, uri, params, options); + } + + public ApiResponse deleteMetadataRule(String externalId, Map options) throws Exception { + if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); + List uri = Arrays.asList("metadata_rules", externalId); + return callApi(HttpMethod.DELETE, uri, ObjectUtils.emptyMap(), options); + } + public ApiResponse analyze(String inputType, String analysisType, String uri, Map options) throws Exception { if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); List url = Arrays.asList("analysis", "analyze", inputType); diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRule.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRule.java new file mode 100644 index 00000000..65edbed4 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRule.java @@ -0,0 +1,66 @@ +package com.cloudinary.metadata; + +import com.cloudinary.utils.ObjectUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class MetadataRule { + String metadataFieldId; + String name; + MetadataRuleCondition condition; + MetadataRuleResult result; + + public MetadataRule(String metadataFieldId, String name, MetadataRuleCondition condition, MetadataRuleResult result) { + this.metadataFieldId = metadataFieldId; + this.name = name; + this.condition = condition; + this.result = result; + } + + public String getMetadataFieldId() { + return metadataFieldId; + } + + public void setMetadataFieldId(String metadataFieldId) { + this.metadataFieldId = metadataFieldId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public MetadataRuleCondition getCondition() { + return condition; + } + + public void setCondition(MetadataRuleCondition condition) { + this.condition = condition; + } + + public MetadataRuleResult getResult() { + return result; + } + + public void setResult(MetadataRuleResult result) { + this.result = result; + } + + public Map asMap() { + Map map = new HashMap(); + map.put("metadata_field_id", getMetadataFieldId()); + map.put("name", getName()); + if (getCondition() != null) { + map.put("condition", ObjectUtils.toJSON(getCondition().asMap())); + } + if(getResult() != null) { + map.put("result", ObjectUtils.toJSON(getResult().asMap())); + } + return map; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleCondition.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleCondition.java new file mode 100644 index 00000000..55e3a714 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleCondition.java @@ -0,0 +1,58 @@ +package com.cloudinary.metadata; +import java.util.HashMap; +import java.util.Map; + +public class MetadataRuleCondition { + String metadata_field_id; + Boolean populated; + Map includes; + String equals; + + public MetadataRuleCondition(String metadata_field_id, Boolean populated, Map includes, String equals) { + this.metadata_field_id = metadata_field_id; + this.populated = populated; + this.includes = includes; + this.equals = equals; + } + + public String getMetadata_field_id() { + return metadata_field_id; + } + + public void setMetadata_field_id(String metadata_field_id) { + this.metadata_field_id = metadata_field_id; + } + + public Boolean getPopulated() { + return populated; + } + + public void setPopulated(Boolean populated) { + this.populated = populated; + } + + public Map getIncludes() { + return includes; + } + + public void setIncludes(Map includes) { + this.includes = includes; + } + + public String getEquals() { + return equals; + } + + public void setEquals(String equals) { + this.equals = equals; + } + + public Map asMap() { + Map result = new HashMap(4); + result.put("metadata_field_id", metadata_field_id); + result.put("populated", populated); + result.put("includes", includes); + result.put("equals", equals); + return result; + } +} diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleResult.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleResult.java new file mode 100644 index 00000000..2d4efff0 --- /dev/null +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleResult.java @@ -0,0 +1,58 @@ +package com.cloudinary.metadata; + +import java.util.HashMap; +import java.util.Map; + +public class MetadataRuleResult { + Boolean enabled; + String activateValues; + String applyValues; + Boolean setMandatory; + + public MetadataRuleResult(Boolean enabled, String activateValues, String applyValues, Boolean setMandatory) { + this.enabled = enabled; + this.activateValues = activateValues; + this.applyValues = applyValues; + this.setMandatory = setMandatory; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public String getActivateValues() { + return activateValues; + } + + public void setActivateValues(String activateValues) { + this.activateValues = activateValues; + } + + public String getApplyValues() { + return applyValues; + } + + public void setApplyValues(String applyValues) { + this.applyValues = applyValues; + } + + public Boolean getSetMandatory() { + return setMandatory; + } + + public void setSetMandatory(Boolean setMandatory) { + this.setMandatory = setMandatory; + } + public Map asMap() { + Map result = new HashMap(4); + result.put("enable", enabled); + result.put("activate_values", activateValues); + result.put("apply_values", applyValues); + result.put("mandatory", setMandatory); + return result; + } +} diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java index bb8f1b9b..d49ac7bb 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java @@ -6,6 +6,7 @@ import com.cloudinary.api.exceptions.BadRequest; import com.cloudinary.metadata.*; +import com.cloudinary.test.helpers.Feature; import com.cloudinary.utils.ObjectUtils; import org.hamcrest.Matchers; import org.junit.*; @@ -196,7 +197,7 @@ public void testReorderMetadataFieldsByLabel() throws Exception { AddStringField("some_value"); AddStringField("aaa"); AddStringField("zzz"); - + ApiResponse result = api.reorderMetadataFields("label", null, Collections.EMPTY_MAP); assertThat(getField(result, 0), Matchers.containsString("aaa")); @@ -309,6 +310,55 @@ public void testSetField() throws Exception { assertNotNull(result); assertEquals(PUBLIC_ID, ((List) result.get("public_ids")).get(0).toString()); } + + @Test + public void testListMetadataRules() throws Exception { + Assume.assumeTrue(MockableTest.shouldTestFeature(Feature.CONDITIONAL_METADATA_RULES)); + ApiResponse result = cloudinary.api().listMetadataRules(null); + assertNotNull(result); + } + + @Test + public void testAddMetadataRule() throws Exception { + Assume.assumeTrue(MockableTest.shouldTestFeature(Feature.CONDITIONAL_METADATA_RULES)); + SetMetadataField field = createSetField("test123"); + ApiResponse response = addFieldToAccount(field); + assertNotNull(response); + + String externalId = (String) response.get("external_id"); + MetadataRule rule = new MetadataRule(externalId, "category-employee", new MetadataRuleCondition("category", false, null, "employee"), new MetadataRuleResult(true, "all", null, null)); + ApiResponse result = cloudinary.api().addMetadataRule(rule, ObjectUtils.asMap()); + assertNotNull(result); + + String name = (String) result.get("name"); + assertEquals(name, "category-employee"); + } + + @Test + public void testUpdateMetadataRule() throws Exception { + Assume.assumeTrue(MockableTest.shouldTestFeature(Feature.CONDITIONAL_METADATA_RULES)); + ApiResponse response = cloudinary.api().listMetadataRules(null); + List metadataRules = (List) response.get("metadata_rules"); + assertNotNull(metadataRules); + String externalId = (String) ((Map) metadataRules.get(0)).get("external_id"); + + MetadataRule rule = new MetadataRule(null, "test_name", null, null); + ApiResponse result = cloudinary.api().updateMetadataRule(externalId, rule, ObjectUtils.asMap()); + assertNotNull(result); + } + + @Test + public void testDeleteMetadataRule() throws Exception { + Assume.assumeTrue(MockableTest.shouldTestFeature(Feature.CONDITIONAL_METADATA_RULES)); + ApiResponse response = cloudinary.api().listMetadataRules(null); + List metadataRules = (List) response.get("metadata_rules"); + assertNotNull(metadataRules); + String externalId = (String) ((Map) metadataRules.get(0)).get("external_id"); + + ApiResponse result = cloudinary.api().deleteMetadataRule(externalId, ObjectUtils.emptyMap()); + assertNotNull(result); + } + // Metadata test helpers private SetMetadataField createSetField(String labelPrefix) { SetMetadataField setField = new SetMetadataField(); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java index aa4297c8..2ced269c 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java @@ -4,4 +4,5 @@ public class Feature { public static final String ALL = "all"; public static final String DYNAMIC_FOLDERS = "dynamic_folders"; public static final String BACKEDUP_ASSETS = "backedup_assets"; + public static final String CONDITIONAL_METADATA_RULES = "conditional_metadata_rules"; } From 2caecfd0273bcfd2bb9cf6b572c9b5b3de37e78c Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Sun, 14 Jul 2024 11:14:35 +0300 Subject: [PATCH 11/40] Version 1.39.0 --- CHANGELOG.md | 13 +++++++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a520f7d..f4b4cc2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +1.39.0 / 2024-07-14 +=================== + +* Add conditional metadata rules api +* Fix rename folder endpoint +* Add config api call +* Add delete backup asset version support +* Add rename folder api support +* Add analyze api +* Add selective response support +* Add access key management +* Add restrictions field to metadata + 1.38.0 / 2024-02-18 =================== diff --git a/README.md b/README.md index b4eb9244..42a635c6 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ For the complete documentation, see the [Java SDK Guide](https://cloudinary.com/ ## Version Support | SDK Version | Java 6+ | |----------------|---------| -| 1.1.0 - 1.38.0 | V | +| 1.1.0 - 1.39.0 | V | ## Installation The cloudinary_java library is available in [Maven Central](https://mvnrepository.com/artifact/com.cloudinary/cloudinary-core). To use it, add the following dependency to your pom.xml : @@ -36,7 +36,7 @@ The cloudinary_java library is available in [Maven Central](https://mvnrepositor com.cloudinary cloudinary-http45 - 1.38.0 + 1.39.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index af553631..24e6f692 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -38,7 +38,7 @@ public class Cloudinary { public final static String AKAMAI_SHARED_CDN = "res.cloudinary.com"; public final static String SHARED_CDN = AKAMAI_SHARED_CDN; - public final static String VERSION = "1.38.0"; + public final static String VERSION = "1.39.0"; static String USER_AGENT_PREFIX = "CloudinaryJava"; public final static String USER_AGENT_JAVA_VERSION = "(Java " + System.getProperty("java.version") + ")"; diff --git a/gradle.properties b/gradle.properties index b3933bac..e588ea7d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.38.0 +version=1.39.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From f4c574389c3689457743ddbd76436a4c35bfff31 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:18:12 +0300 Subject: [PATCH 12/40] Add support for update metadata field set default disabled --- .../cloudinary/metadata/MetadataField.java | 9 ++++++ .../com/cloudinary/test/AbstractApiTest.java | 6 ++-- .../test/AbstractStructuredMetadataTest.java | 30 ++++++++++--------- .../cloudinary/test/AbstractUploaderTest.java | 2 +- .../cloudinary/test/MetadataTestHelper.java | 4 +-- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java index 7dee301f..4f3bdf33 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java @@ -17,6 +17,7 @@ public class MetadataField extends JSONObject { public static final String TYPE = "type"; public static final String VALIDATION = "validation"; public static final String RESTRICTIONS = "restrictions"; + public static final String DEFAULT_DISABLED = "default_disabled"; public MetadataField(MetadataFieldType type) { put(TYPE, type.toString()); @@ -139,4 +140,12 @@ public void setDataSource(MetadataDataSource dataSource) { public void setRestrictions(Restrictions restrictions) { put(RESTRICTIONS, restrictions.toHash()); } + + /** + * Set the value indicating whether the field should be disabled by default + * @param disabled The value to set. + */ + public void setDefaultDisabled(Boolean disabled) { + put(DEFAULT_DISABLED, disabled); + } } \ No newline at end of file diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index cd1ccc88..dd3802c9 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -415,7 +415,7 @@ public void testDeleteDerivedByTransformation() throws Exception { @Test public void testGetResourcesWithMetadata() throws Exception { String public_id = "api_,withMetadata" + SUFFIX; - String fieldId = MetadataTestHelper.addFieldToAccount(api, MetadataTestHelper.newFieldInstance("some_field" + SUFFIX)).get("external_id").toString(); + String fieldId = MetadataTestHelper.addFieldToAccount(api, MetadataTestHelper.newFieldInstance("some_field" + SUFFIX, true)).get("external_id").toString(); cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", public_id, "tags", UPLOAD_TAGS, @@ -761,8 +761,8 @@ public void testDetectionUpdate() { @Test public void testUpdateResourceClearInvalid() throws Exception { - String fieldId = MetadataTestHelper.addFieldToAccount(api, MetadataTestHelper.newFieldInstance("some_field3" + SUFFIX)).get("external_id").toString(); - String fieldId2 = MetadataTestHelper.addFieldToAccount(api, MetadataTestHelper.newFieldInstance("some_field4" + SUFFIX)).get("external_id").toString(); + String fieldId = MetadataTestHelper.addFieldToAccount(api, MetadataTestHelper.newFieldInstance("some_field3" + SUFFIX, true)).get("external_id").toString(); + String fieldId2 = MetadataTestHelper.addFieldToAccount(api, MetadataTestHelper.newFieldInstance("some_field4" + SUFFIX, true)).get("external_id").toString(); Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("tags", UPLOAD_TAGS, "metadata", ObjectUtils.asMap(fieldId, "test"))); Map apiResult = api.update((String) uploadResult.get("public_id"), ObjectUtils.asMap("clear_invalid", true, "metadata", ObjectUtils.asMap(fieldId2, "test2"))); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java index d49ac7bb..241ac47d 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java @@ -62,7 +62,7 @@ public void setUp() { @Test public void testCreateMetadata() throws Exception { - StringMetadataField stringField = newFieldInstance("testCreateMetadata_1"); + StringMetadataField stringField = newFieldInstance("testCreateMetadata_1", true); ApiResponse result = addFieldToAccount(stringField); assertNotNull(result); assertEquals(stringField.getLabel(), result.get("label")); @@ -75,7 +75,7 @@ public void testCreateMetadata() throws Exception { @Test public void testFieldRestrictions() throws Exception { - StringMetadataField stringField = newFieldInstance("testCreateMetadata_3"); + StringMetadataField stringField = newFieldInstance("testCreateMetadata_3", true); stringField.setRestrictions(new Restrictions().setReadOnlyUI()); ApiResponse result = api.addMetadataField(stringField); @@ -124,7 +124,7 @@ public void testDateFieldDefaultValueValidation() throws Exception { @Test public void testListFields() throws Exception { - StringMetadataField stringField = newFieldInstance("testListFields"); + StringMetadataField stringField = newFieldInstance("testListFields", true); addFieldToAccount(stringField); ApiResponse result = cloudinary.api().listMetadataFields(); @@ -135,7 +135,7 @@ public void testListFields() throws Exception { @Test public void testGetMetadata() throws Exception { - ApiResponse fieldResult = addFieldToAccount(newFieldInstance("testGetMetadata")); + ApiResponse fieldResult = addFieldToAccount(newFieldInstance("testGetMetadata", true)); ApiResponse result = api.metadataFieldByFieldId(fieldResult.get("external_id").toString()); assertNotNull(result); assertEquals(fieldResult.get("label"), result.get("label")); @@ -143,14 +143,16 @@ public void testGetMetadata() throws Exception { @Test public void testUpdateField() throws Exception { - StringMetadataField metadataField = newFieldInstance("testUpdateField"); + StringMetadataField metadataField = newFieldInstance("testUpdateField", false); ApiResponse fieldResult = addFieldToAccount(metadataField); assertNotEquals("new_def", fieldResult.get("default_value")); metadataField.setDefaultValue("new_def"); + metadataField.setDefaultDisabled(true); metadataField.setRestrictions(new Restrictions().setReadOnlyUI()); ApiResponse result = api.updateMetadataField(fieldResult.get("external_id").toString(), metadataField); assertNotNull(result); assertEquals("new_def", result.get("default_value")); + assertEquals(true, result.get("default_disabled")); Map restrictions = (Map) result.get("restrictions"); assertNotNull(restrictions); assertTrue((Boolean)restrictions.get("readonly_ui")); @@ -158,7 +160,7 @@ public void testUpdateField() throws Exception { @Test public void testDeleteField() throws Exception { - ApiResponse fieldResult = addFieldToAccount(newFieldInstance("testDeleteField")); + ApiResponse fieldResult = addFieldToAccount(newFieldInstance("testDeleteField", true)); ApiResponse result = api.deleteMetadataField(fieldResult.get("external_id").toString()); assertNotNull(result); assertEquals("ok", result.get("message")); @@ -219,14 +221,14 @@ private String getField(ApiResponse result, int index) { } private void AddStringField(String labelPrefix) throws Exception { - StringMetadataField field = newFieldInstance(labelPrefix); + StringMetadataField field = newFieldInstance(labelPrefix, true); ApiResponse fieldResult = addFieldToAccount(field); String fieldId = fieldResult.get("external_id").toString(); } @Test public void testUploadWithMetadata() throws Exception { - StringMetadataField field = newFieldInstance("testUploadWithMetadata"); + StringMetadataField field = newFieldInstance("testUploadWithMetadata", true); ApiResponse fieldResult = addFieldToAccount(field); String fieldId = fieldResult.get("external_id").toString(); Map metadata = Collections.singletonMap(fieldId, "123456"); @@ -239,7 +241,7 @@ public void testUploadWithMetadata() throws Exception { public void testExplicitWithMetadata() throws Exception { Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", Arrays.asList(SDK_TEST_TAG, METADATA_UPLOADER_TAG))); String publicId = uploadResult.get("public_id").toString(); - StringMetadataField field = newFieldInstance("testExplicitWithMetadata"); + StringMetadataField field = newFieldInstance("testExplicitWithMetadata", true); ApiResponse fieldResult = addFieldToAccount(field); String fieldId = fieldResult.get("external_id").toString(); Map metadata = Collections.singletonMap(fieldId, "123456"); @@ -263,7 +265,7 @@ public void testExplicitWithMetadata() throws Exception { public void testUpdateWithMetadata() throws Exception { Map uploadResult = cloudinary.uploader().upload(SRC_TEST_IMAGE, asMap("tags", Arrays.asList(SDK_TEST_TAG, METADATA_UPLOADER_TAG))); String publicId = uploadResult.get("public_id").toString(); - StringMetadataField field = newFieldInstance("testUpdateWithMetadata"); + StringMetadataField field = newFieldInstance("testUpdateWithMetadata", true); ApiResponse fieldResult = addFieldToAccount(field); String fieldId = fieldResult.get("external_id").toString(); Map metadata = Collections.singletonMap(fieldId, "123456"); @@ -274,7 +276,7 @@ public void testUpdateWithMetadata() throws Exception { @Test public void testUploaderUpdateMetadata() throws Exception { - StringMetadataField field = newFieldInstance("testUploaderUpdateMetadata"); + StringMetadataField field = newFieldInstance("testUploaderUpdateMetadata", true); ApiResponse fieldResult = addFieldToAccount(field); String fieldId = fieldResult.get("external_id").toString(); Map result = cloudinary.uploader().updateMetadata(Collections.singletonMap(fieldId, "123456"), new String[]{PUBLIC_ID}, null); @@ -288,7 +290,7 @@ public void testUploaderUpdateMetadata() throws Exception { @Test public void testUploaderUpdateMetadataClearInvalid() throws Exception { - StringMetadataField field = newFieldInstance("testUploaderUpdateMetadata1"); + StringMetadataField field = newFieldInstance("testUploaderUpdateMetadata1", true); ApiResponse fieldResult = addFieldToAccount(field); String fieldId = fieldResult.get("external_id").toString(); Map result = cloudinary.uploader().updateMetadata(Collections.singletonMap(fieldId, "123456"), new String[]{PUBLIC_ID}, ObjectUtils.asMap("clear_invalid", true)); @@ -377,9 +379,9 @@ private SetMetadataField createSetField(String labelPrefix) { return setField; } - private StringMetadataField newFieldInstance(String labelPrefix) throws Exception { + private StringMetadataField newFieldInstance(String labelPrefix, Boolean mandatory) throws Exception { String label = labelPrefix + "_" + SUFFIX; - return MetadataTestHelper.newFieldInstance(label); + return MetadataTestHelper.newFieldInstance(label, mandatory); } private ApiResponse addFieldToAccount(MetadataField field) throws Exception { diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java index bddf48cc..99654c4e 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java @@ -236,7 +236,7 @@ public void testRenameShouldReturnContext() throws Exception { @Test public void testRenameShouldReturnMetadata() throws Exception { String label = "test" + SUFFIX; - StringMetadataField f = MetadataTestHelper.newFieldInstance(label); + StringMetadataField f = MetadataTestHelper.newFieldInstance(label, true); Map fieldResult = MetadataTestHelper.addFieldToAccount(cloudinary.api(), f); String fieldId = fieldResult.get("external_id").toString(); Map metadata = Collections.singletonMap(fieldId, "123456"); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java index d130b282..cdb52487 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java @@ -7,10 +7,10 @@ import com.cloudinary.metadata.StringMetadataField; public class MetadataTestHelper { - public static StringMetadataField newFieldInstance(String label) throws Exception { + public static StringMetadataField newFieldInstance(String label, Boolean mandatory) throws Exception { StringMetadataField field = new StringMetadataField(); field.setLabel(label); - field.setMandatory(true); + field.setMandatory(mandatory); field.setValidation(new MetadataValidation.StringLength(3, 9)); field.setDefaultValue("val_test"); return field; From 5553c36e22ecff46ced3db4a8c2157629816269f Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 30 Jul 2024 17:42:55 +0300 Subject: [PATCH 13/40] Implement major version requirements --- .../java/com/cloudinary/Configuration.java | 4 +- .../java/com/cloudinary/AuthTokenTest.java | 16 +-- .../cloudinary/analytics/AnalyticsTest.java | 16 +-- .../com/cloudinary/test/CloudinaryTest.java | 113 +++++++++--------- .../cloudinary/transformation/LayerTest.java | 6 +- .../cloudinary/test/AbstractUploaderTest.java | 2 + 6 files changed, 80 insertions(+), 77 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index b0dcc41f..07280d89 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -114,7 +114,7 @@ public void update(Map config) { this.apiSecret = (String) config.get("api_secret"); this.secureDistribution = (String) config.get("secure_distribution"); this.cname = (String) config.get("cname"); - this.secure = ObjectUtils.asBoolean(config.get("secure"), false); + this.secure = ObjectUtils.asBoolean(config.get("secure"), true); this.privateCdn = ObjectUtils.asBoolean(config.get("private_cdn"), false); this.cdnSubdomain = ObjectUtils.asBoolean(config.get("cdn_subdomain"), false); this.shorten = ObjectUtils.asBoolean(config.get("shorten"), false); @@ -128,7 +128,7 @@ public void update(Map config) { this.loadStrategies = ObjectUtils.asBoolean(config.get("load_strategies"), true); this.timeout = ObjectUtils.asInteger(config.get("timeout"), 0); this.clientHints = ObjectUtils.asBoolean(config.get("client_hints"), false); - this.analytics = ObjectUtils.asBoolean(config.get("analytics"), null); + this.analytics = ObjectUtils.asBoolean(config.get("analytics"), true); Map tokenMap = (Map) config.get("auth_token"); if (tokenMap != null) { this.authToken = new AuthToken(tokenMap); diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index ca30479e..468c43bb 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -32,7 +32,7 @@ public class AuthTokenTest { @Before public void setUp() { System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); - this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); + this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false&analytics=false"); final AuthToken authToken = new AuthToken(KEY).duration(300); authToken.startTime(11111111); // start time is set for test purposes cloudinary.config.authToken = authToken; @@ -74,28 +74,28 @@ public void testAuthenticatedUrl() { String message = "should add token if authToken is globally set and signed = true"; String url = cloudinary.url().signed(true).resourceType("image").type("authenticated").version("1486020273").generate("sample.jpg"); - assertEquals(message,"http://test123-res.cloudinary.com/image/authenticated/v1486020273/sample.jpg?__cld_token__=st=11111111~exp=11111411~hmac=8db0d753ee7bbb9e2eaf8698ca3797436ba4c20e31f44527e43b6a6e995cfdb3", url); + assertEquals(message,"https://test123-res.cloudinary.com/image/authenticated/v1486020273/sample.jpg?__cld_token__=st=11111111~exp=11111411~hmac=8db0d753ee7bbb9e2eaf8698ca3797436ba4c20e31f44527e43b6a6e995cfdb3", url); message = "should add token for 'public' resource"; url = cloudinary.url().signed(true).resourceType("image").type("public").version("1486020273").generate("sample.jpg"); - assertEquals(message,"http://test123-res.cloudinary.com/image/public/v1486020273/sample.jpg?__cld_token__=st=11111111~exp=11111411~hmac=c2b77d9f81be6d89b5d0ebc67b671557e88a40bcf03dd4a6997ff4b994ceb80e", url); + assertEquals(message,"https://test123-res.cloudinary.com/image/public/v1486020273/sample.jpg?__cld_token__=st=11111111~exp=11111411~hmac=c2b77d9f81be6d89b5d0ebc67b671557e88a40bcf03dd4a6997ff4b994ceb80e", url); message = "should not add token if signed is false"; url = cloudinary.url().resourceType("image").type("authenticated").version("1486020273").generate("sample.jpg"); - assertEquals(message,"http://test123-res.cloudinary.com/image/authenticated/v1486020273/sample.jpg", url); + assertEquals(message,"https://test123-res.cloudinary.com/image/authenticated/v1486020273/sample.jpg", url); message = "should not add token if authToken is globally set but null auth token is explicitly set and signed = true"; url = cloudinary.url().authToken(AuthToken.NULL_AUTH_TOKEN).signed(true).resourceType("image").type("authenticated").version("1486020273").generate("sample.jpg"); - assertEquals(message,"http://test123-res.cloudinary.com/image/authenticated/s--v2fTPYTu--/v1486020273/sample.jpg", url); + assertEquals(message,"https://test123-res.cloudinary.com/image/authenticated/s--v2fTPYTu--/v1486020273/sample.jpg", url); message = "explicit authToken should override global setting"; url = cloudinary.url().signed(true).authToken(new AuthToken(ALT_KEY).startTime(222222222).duration(100)).resourceType("image").type("authenticated").transformation(new Transformation().crop("scale").width(300)).generate("sample.jpg"); - assertEquals(message,"http://test123-res.cloudinary.com/image/authenticated/c_scale,w_300/sample.jpg?__cld_token__=st=222222222~exp=222222322~hmac=55cfe516530461213fe3b3606014533b1eca8ff60aeab79d1bb84c9322eebc1f", url); + assertEquals(message,"https://test123-res.cloudinary.com/image/authenticated/c_scale,w_300/sample.jpg?__cld_token__=st=222222222~exp=222222322~hmac=55cfe516530461213fe3b3606014533b1eca8ff60aeab79d1bb84c9322eebc1f", url); message = "should compute expiration as start time + duration"; url = cloudinary.url().signed(true).authToken(new AuthToken().startTime(11111111).duration(300)) .type("authenticated").version("1486020273").generate("sample.jpg"); - assertEquals(message,"http://test123-res.cloudinary.com/image/authenticated/v1486020273/sample.jpg?__cld_token__=st=11111111~exp=11111411~hmac=8db0d753ee7bbb9e2eaf8698ca3797436ba4c20e31f44527e43b6a6e995cfdb3", url); + assertEquals(message,"https://test123-res.cloudinary.com/image/authenticated/v1486020273/sample.jpg?__cld_token__=st=11111111~exp=11111411~hmac=8db0d753ee7bbb9e2eaf8698ca3797436ba4c20e31f44527e43b6a6e995cfdb3", url); } @@ -120,7 +120,7 @@ public void testTokenGeneration(){ public void testUrlInTag() { String message = "should add token to an image tag url"; String url = cloudinary.url().signed(true).resourceType("image").type("authenticated").version("1486020273").imageTag("sample.jpg"); - assertThat(url, Matchers.matchesPattern("")); + assertThat(url, Matchers.matchesPattern("")); } diff --git a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java index 6300947e..e87143c6 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/analytics/AnalyticsTest.java @@ -65,26 +65,28 @@ public void testUrlWithAnalytics() { cloudinary.config.analytics = true; cloudinary.setAnalytics(new Analytics("F", "2.0.0", "1.8.0", "Z", "1.34.0", "0")); String url = cloudinary.url().generate("test"); - Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=DAFAACMhZBi0"); + Assert.assertEquals(url, "https://res.cloudinary.com/test123/image/upload/test?_a=DAFAACMhZBi0"); } @Test public void testUrlWithNoAnalytics() { - String url = cloudinary.url().generate("test"); - Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); + cloudinary.config.analytics = false; + String url = cloudinary.url().secure(true).generate("test"); + Assert.assertEquals(url, "https://res.cloudinary.com/test123/image/upload/test"); } @Test public void testUrlWithNoAnalyticsDefined() { cloudinary.config.analytics = false; String url = cloudinary.url().generate("test"); - Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); + Assert.assertEquals(url, "https://res.cloudinary.com/test123/image/upload/test"); } @Test public void testUrlWithNoAnalyticsNull() { + cloudinary.config.analytics = false; String url = cloudinary.url().generate("test"); - Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test"); + Assert.assertEquals(url, "https://res.cloudinary.com/test123/image/upload/test"); } @Test @@ -93,7 +95,7 @@ public void testUrlWithNoAnalyticsNullAndTrue() { cloudinary.analytics.setSDKSemver("1.30.0"); cloudinary.analytics.setTechVersion("12.0.0"); String url = cloudinary.url().generate("test"); - Assert.assertEquals(url, "http://res.cloudinary.com/test123/image/upload/test?_a=DAGAu5AMZAA0"); + Assert.assertEquals(url, "https://res.cloudinary.com/test123/image/upload/test?_a=DAGAu5AMZAA0"); } @Test @@ -123,7 +125,7 @@ public void testUrlNoAnalyticsWithQueryParams() { cloudinary.setAnalytics(new Analytics("F", "2.0.0", System.getProperty("java.version"), "Z", System.getProperty("os.version"), "0")); cloudinary.config.privateCdn = true; String url = cloudinary.url().signed(true).type("authenticated").generate("test"); - assertEquals(url,"http://test123-res.cloudinary.com/image/authenticated/test?__cld_token__=st=11111111~exp=11111411~hmac=735a49389a72ac0b90d1a84ac5d43facd1a9047f153b39e914747ef6ed195e53"); + assertEquals(url,"https://test123-res.cloudinary.com/image/authenticated/test?__cld_token__=st=11111111~exp=11111411~hmac=735a49389a72ac0b90d1a84ac5d43facd1a9047f153b39e914747ef6ed195e53"); cloudinary.config.privateCdn = false; } diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java index f23acd60..0721acae 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -35,7 +35,7 @@ @RunWith(JUnitParamsRunner.class) public class CloudinaryTest { - private static final String DEFAULT_ROOT_PATH = "http://res.cloudinary.com/test123/"; + private static final String DEFAULT_ROOT_PATH = "https://res.cloudinary.com/test123/"; private static final String DEFAULT_UPLOAD_PATH = DEFAULT_ROOT_PATH + "image/upload/"; private static final String VIDEO_UPLOAD_PATH = DEFAULT_ROOT_PATH + "video/upload/"; private Cloudinary cloudinary; @@ -46,7 +46,7 @@ public class CloudinaryTest { @Before public void setUp() { System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); - this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); + this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false&analytics=false"); } @Test @@ -93,13 +93,13 @@ public void testCloudName() { public void testCloudNameOptions() { // should allow overriding cloud_name in options String result = cloudinary.url().cloudName("test321").generate("test"); - assertEquals("http://res.cloudinary.com/test321/image/upload/test", result); + assertEquals("https://res.cloudinary.com/test321/image/upload/test", result); } @Test public void testSecureDistribution() { // should use default secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).generate("test"); + String result = cloudinary.url().generate("test"); assertEquals("https://res.cloudinary.com/test123/image/upload/test", result); } @@ -113,7 +113,7 @@ public void testTextLayerStyleIdentifierVariables() { new TextLayer().text("hello-world").textStyle("$style") )).generate("sample"); - assertEquals("http://res.cloudinary.com/test123/image/upload/$style_!Arial_12!/l_text:$style:hello-world/sample", url); + assertEquals("https://res.cloudinary.com/test123/image/upload/$style_!Arial_12!/l_text:$style:hello-world/sample", url); url = cloudinary.url().transformation( new Transformation() @@ -123,14 +123,14 @@ public void testTextLayerStyleIdentifierVariables() { new TextLayer().text("hello-world").textStyle(new Expression("$style")) )).generate("sample"); - assertEquals("http://res.cloudinary.com/test123/image/upload/$style_!Arial_12!/l_text:$style:hello-world/sample", url); + assertEquals("https://res.cloudinary.com/test123/image/upload/$style_!Arial_12!/l_text:$style:hello-world/sample", url); } @Test public void testSecureDistributionOverwrite() { // should allow overwriting secure distribution if secure=TRUE - String result = cloudinary.url().secure(true).secureDistribution("something.else.com").generate("test"); + String result = cloudinary.url().secureDistribution("something.else.com").generate("test"); assertEquals("https://something.else.com/test123/image/upload/test", result); } @@ -146,7 +146,6 @@ public void testSecureDistibution() { public void testSecureAkamai() { // should default to akamai if secure is given with private_cdn and no // secure_distribution - cloudinary.config.secure = true; cloudinary.config.privateCdn = true; String result = cloudinary.url().generate("test"); assertEquals("https://test123-res.cloudinary.com/image/upload/test", result); @@ -156,7 +155,6 @@ public void testSecureAkamai() { public void testSecureNonAkamai() { // should not add cloud_name if private_cdn and secure non akamai // secure_distribution - cloudinary.config.secure = true; cloudinary.config.privateCdn = true; cloudinary.config.secureDistribution = "something.cloudfront.net"; String result = cloudinary.url().generate("test"); @@ -168,7 +166,7 @@ public void testHttpPrivateCdn() { // should not add cloud_name if private_cdn and not secure cloudinary.config.privateCdn = true; String result = cloudinary.url().generate("test"); - assertEquals("http://test123-res.cloudinary.com/image/upload/test", result); + assertEquals("https://test123-res.cloudinary.com/image/upload/test", result); } @Test @@ -182,14 +180,14 @@ public void testFormat() { public void testType() { // should use type from options String result = cloudinary.url().type("facebook").generate("test"); - assertEquals("http://res.cloudinary.com/test123/image/facebook/test", result); + assertEquals("https://res.cloudinary.com/test123/image/facebook/test", result); } @Test public void testResourceType() { // should use resource_type from options String result = cloudinary.url().resourcType("raw").generate("test"); - assertEquals("http://res.cloudinary.com/test123/raw/upload/test", result); + assertEquals("https://res.cloudinary.com/test123/raw/upload/test", result); } @Test @@ -200,27 +198,27 @@ public void testIgnoreHttp() { result = cloudinary.url().type("asset").generate("http://test"); assertEquals("http://test", result); result = cloudinary.url().type("fetch").generate("http://test"); - assertEquals("http://res.cloudinary.com/test123/image/fetch/http://test", result); + assertEquals("https://res.cloudinary.com/test123/image/fetch/http://test", result); } @Test public void testFetch() { // should escape fetch urls String result = cloudinary.url().type("fetch").generate("http://blah.com/hello?a=b"); - assertEquals("http://res.cloudinary.com/test123/image/fetch/http://blah.com/hello%3Fa%3Db", result); + assertEquals("https://res.cloudinary.com/test123/image/fetch/http://blah.com/hello%3Fa%3Db", result); } @Test public void testCname() { // should support external cname - String result = cloudinary.url().cname("hello.com").generate("test"); + String result = cloudinary.url().cname("hello.com").secure(false).generate("test"); assertEquals("http://hello.com/test123/image/upload/test", result); } @Test public void testCnameSubdomain() { // should support external cname with cdn_subdomain on - String result = cloudinary.url().cname("hello.com").cdnSubdomain(true).generate("test"); + String result = cloudinary.url().cname("hello.com").cdnSubdomain(true).secure(false).generate("test"); assertEquals("http://a2.hello.com/test123/image/upload/test", result); } @@ -243,17 +241,17 @@ public void testDisallowUrlSuffixWithDot() { @Test public void testSupportUrlSuffixForPrivateCdn() { String actual = cloudinary.url().suffix("hello").privateCdn(true).generate("test"); - assertEquals("http://test123-res.cloudinary.com/images/test/hello", actual); + assertEquals("https://test123-res.cloudinary.com/images/test/hello", actual); actual = cloudinary.url().suffix("hello").privateCdn(true).transformation(new Transformation().angle(0)).generate("test"); - assertEquals("http://test123-res.cloudinary.com/images/a_0/test/hello", actual); + assertEquals("https://test123-res.cloudinary.com/images/a_0/test/hello", actual); } @Test public void testPutFormatAfterUrlSuffix() { String actual = cloudinary.url().suffix("hello").privateCdn(true).format("jpg").generate("test"); - assertEquals("http://test123-res.cloudinary.com/images/test/hello.jpg", actual); + assertEquals("https://test123-res.cloudinary.com/images/test/hello.jpg", actual); } @Test @@ -266,7 +264,7 @@ public void testNotSignTheUrlSuffix() { String expectedSignature = url.substring(matcher.start(), matcher.end()); String actual = cloudinary.url().format("jpg").privateCdn(true).signed(true).suffix("hello").generate("test"); - assertEquals("http://test123-res.cloudinary.com/images/" + expectedSignature + "/test/hello.jpg", actual); + assertEquals("https://test123-res.cloudinary.com/images/" + expectedSignature + "/test/hello.jpg", actual); url = cloudinary.url().format("jpg").signed(true).transformation(new Transformation().angle(0)).generate("test"); matcher = pattern.matcher(url); @@ -275,56 +273,56 @@ public void testNotSignTheUrlSuffix() { actual = cloudinary.url().format("jpg").privateCdn(true).signed(true).suffix("hello").transformation(new Transformation().angle(0)).generate("test"); - assertEquals("http://test123-res.cloudinary.com/images/" + expectedSignature + "/a_0/test/hello.jpg", actual); + assertEquals("https://test123-res.cloudinary.com/images/" + expectedSignature + "/a_0/test/hello.jpg", actual); } @Test public void testSignatureLength(){ String url = cloudinary.url().signed(true).generate("sample.jpg"); - assertEquals("http://res.cloudinary.com/test123/image/upload/s--v2fTPYTu--/sample.jpg", url); + assertEquals("https://res.cloudinary.com/test123/image/upload/s--v2fTPYTu--/sample.jpg", url); url = cloudinary.url().signed(true).longUrlSignature(true).generate("sample.jpg"); - assertEquals("http://res.cloudinary.com/test123/image/upload/s--2hbrSMPOjj5BJ4xV7SgFbRDevFaQNUFf--/sample.jpg", url); + assertEquals("https://res.cloudinary.com/test123/image/upload/s--2hbrSMPOjj5BJ4xV7SgFbRDevFaQNUFf--/sample.jpg", url); } @Test public void testSupportUrlSuffixForRawUploads() { String actual = cloudinary.url().suffix("hello").privateCdn(true).resourceType("raw").generate("test"); - assertEquals("http://test123-res.cloudinary.com/files/test/hello", actual); + assertEquals("https://test123-res.cloudinary.com/files/test/hello", actual); } @Test public void testSupportUrlSuffixForVideoUploads() { String actual = cloudinary.url().suffix("hello").privateCdn(true).resourceType("video").generate("test"); - assertEquals("http://test123-res.cloudinary.com/videos/test/hello", actual); + assertEquals("https://test123-res.cloudinary.com/videos/test/hello", actual); } @Test public void testSupportUrlSuffixForAuthenticatedImages() { String actual = cloudinary.url().suffix("hello").privateCdn(true).resourceType("image").type("authenticated").generate("test"); - assertEquals("http://test123-res.cloudinary.com/authenticated_images/test/hello", actual); + assertEquals("https://test123-res.cloudinary.com/authenticated_images/test/hello", actual); } @Test public void testSupportUrlSuffixForPrivateImages() { String actual = cloudinary.url().suffix("hello").privateCdn(true).resourceType("image").type("private").generate("test"); - assertEquals("http://test123-res.cloudinary.com/private_images/test/hello", actual); + assertEquals("https://test123-res.cloudinary.com/private_images/test/hello", actual); } @Test public void testSupportUseRootPathForPrivateCdn() { String actual = cloudinary.url().privateCdn(true).useRootPath(true).generate("test"); - assertEquals("http://test123-res.cloudinary.com/test", actual); + assertEquals("https://test123-res.cloudinary.com/test", actual); actual = cloudinary.url().privateCdn(true).transformation(new Transformation().angle(0)).useRootPath(true).generate("test"); - assertEquals("http://test123-res.cloudinary.com/a_0/test", actual); + assertEquals("https://test123-res.cloudinary.com/a_0/test", actual); } @Test public void testSupportUseRootPathTogetherWithUrlSuffixForPrivateCdn() { String actual = cloudinary.url().privateCdn(true).suffix("hello").useRootPath(true).generate("test"); - assertEquals("http://test123-res.cloudinary.com/test/hello", actual); + assertEquals("https://test123-res.cloudinary.com/test/hello", actual); } @@ -452,7 +450,7 @@ public void testNoEmptyTransformation() { public void testHttpEscape() { // should escape http urls String result = cloudinary.url().type("youtube").generate("http://www.youtube.com/watch?v=d9NF2edxy-M"); - assertEquals("http://res.cloudinary.com/test123/image/youtube/http://www.youtube.com/watch%3Fv%3Dd9NF2edxy-M", result); + assertEquals("https://res.cloudinary.com/test123/image/youtube/http://www.youtube.com/watch%3Fv%3Dd9NF2edxy-M", result); } @Test @@ -489,14 +487,14 @@ public void testAngle() { public void testFetchFormat() { // should support format for fetch urls String result = cloudinary.url().format("jpg").type("fetch").generate("http://cloudinary.com/images/old_logo.png"); - assertEquals("http://res.cloudinary.com/test123/image/fetch/f_jpg/http://cloudinary.com/images/old_logo.png", result); + assertEquals("https://res.cloudinary.com/test123/image/fetch/f_jpg/http://cloudinary.com/images/old_logo.png", result); } @Test public void testUseFetchFormat() { // should support use fetch format, adds the format but not an extension String result = cloudinary.url().format("jpg").useFetchFormat(true).generate("old_logo"); - assertEquals("http://res.cloudinary.com/test123/image/upload/f_jpg/old_logo", result); + assertEquals("https://res.cloudinary.com/test123/image/upload/f_jpg/old_logo", result); } @Test @@ -580,24 +578,24 @@ public void testOpacity() { public void testImageTag() { Transformation transformation = new Transformation().width(100).height(101).crop("crop"); String result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image")); - assertEquals("my image", result); + assertEquals("my image", result); transformation = new Transformation().width(0.9).height(0.9).crop("crop").responsiveWidth(true); result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image")); assertEquals( - "my image", + "my image", result); result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image", "class", "extra")); assertEquals( - "my image", + "my image", result); transformation = new Transformation().width("auto").crop("crop"); result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image", "responsive_placeholder", "blank")); assertEquals( - "my image", + "my image", result); result = cloudinary.url().transformation(transformation).imageTag("test", asMap("alt", "my image", "responsive_placeholder", "other.gif")); assertEquals( - "my image", + "my image", result); } @@ -614,12 +612,12 @@ public void testClientHints() { assertTrue(testTag.startsWith(" getUrlParameters(URI uri) throws UnsupportedEn @Test public void testUrlCloneConfig() { // verify that secure (from url.config) is cloned as well: - Url url = cloudinary.url().cloudName("cloud").format("frmt").publicId("123").secure(true); + Url url = cloudinary.url().cloudName("cloud").format("frmt").publicId("123"); assertEquals("https://res.cloudinary.com/cloud/image/upload/123.frmt", url.clone().generate()); } diff --git a/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java b/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java index d801c4dc..ca230f52 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/transformation/LayerTest.java @@ -12,14 +12,14 @@ * Created by amir on 03/11/2015. */ public class LayerTest { - private static final String DEFAULT_ROOT_PATH = "http://res.cloudinary.com/test123/"; + private static final String DEFAULT_ROOT_PATH = "https://res.cloudinary.com/test123/"; private static final String DEFAULT_UPLOAD_PATH = DEFAULT_ROOT_PATH + "image/upload/"; private static final String VIDEO_UPLOAD_PATH = DEFAULT_ROOT_PATH + "video/upload/"; private Cloudinary cloudinary; @Before public void setUp() { - this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false"); + this.cloudinary = new Cloudinary("cloudinary://a:b@test123?load_strategies=false&analytics=false"); } @After @@ -46,7 +46,7 @@ public void testOverlay() { } @Test - public void testUnderlay() { + public void testUnderlay() { Transformation transformation = new Transformation().underlay("text:hello"); String result = cloudinary.url().transformation(transformation).generate("test"); assertEquals(DEFAULT_UPLOAD_PATH + "u_text:hello/test", result); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java index 99654c4e..8581ca30 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java @@ -41,6 +41,7 @@ abstract public class AbstractUploaderTest extends MockableTest { @BeforeClass public static void setUpClass() throws IOException { Cloudinary cloudinary = new Cloudinary(); + cloudinary.config.analytics = false; if (cloudinary.config.apiSecret == null) { System.err.println("Please setup environment for Upload test to run"); } @@ -89,6 +90,7 @@ public static void tearDownClass() { public void setUp() { System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); this.cloudinary = new Cloudinary(); + this.cloudinary.config.analytics = false; assumeNotNull(cloudinary.config.apiSecret); } From 238b54971b153b318b7ffc04823042fd1eb7d745 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 30 Jul 2024 17:48:55 +0300 Subject: [PATCH 14/40] Update minimum java version --- java_shared.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java_shared.gradle b/java_shared.gradle index d23a49b4..f7e6e550 100644 --- a/java_shared.gradle +++ b/java_shared.gradle @@ -1,5 +1,5 @@ -sourceCompatibility = 1.7 -targetCompatibility = 1.7 +sourceCompatibility = 1.8 +targetCompatibility = 1.8 javadoc { options.encoding = 'UTF-8' From 232a2e538331c2d4011215dfe61b10fdf2d21b9e Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 30 Jul 2024 17:52:24 +0300 Subject: [PATCH 15/40] Remove deprecated functions --- .../main/java/com/cloudinary/Cloudinary.java | 5 ----- .../src/main/java/com/cloudinary/Uploader.java | 5 ----- .../transformation/AbstractLayerBuilder.java | 7 ------- .../cloudinary/transformation/Condition.java | 17 ----------------- .../cloudinary/transformation/LayerBuilder.java | 7 ------- .../transformation/SubtitlesLayerBuilder.java | 7 ------- .../transformation/TextLayerBuilder.java | 7 ------- .../com/cloudinary/test/CloudinaryTest.java | 9 +++++---- .../cloudinary/taglib/CloudinaryImageTag.java | 11 ----------- 9 files changed, 5 insertions(+), 70 deletions(-) delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java delete mode 100644 cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 24e6f692..3f636d06 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -401,9 +401,4 @@ private String buildUrl(String base, Map params) throws Unsuppor } return urlBuilder.toString(); } - - @Deprecated - public static Map asMap(Object... values) { - return ObjectUtils.asMap(values); - } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java index d2ae11ea..39b21950 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Uploader.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Uploader.java @@ -263,11 +263,6 @@ public Map explicit(String publicId, Map options) throws IOException { return callApi("explicit", params, options, null); } - @Deprecated - public Map generate_sprite(String tag, Map options) throws IOException { - return generateSprite(tag, options); - } - public Map generateSprite(String tag, Map options) throws IOException { if (options == null) options = Collections.singletonMap("tag", tag); diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java deleted file mode 100644 index bcc2cfea..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayerBuilder.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.cloudinary.transformation; - -/** - * @deprecated - */ -public abstract class AbstractLayerBuilder extends AbstractLayer { -} diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java index 35613813..234fd477 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java +++ b/cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java @@ -65,27 +65,10 @@ public Condition initialDuration(String operator, Object value) { return predicate("idu", operator, value); } - - /** - * @deprecated Use {@link #faceCount(String, Object)} instead - */ - @Deprecated - public Condition faces(String operator, Object value) { - return faceCount(operator, value); - } - public Condition faceCount(String operator, Object value) { return predicate("fc", operator, value); } - /** - * @deprecated Use {@link #pageCount(String, Object)} instead - */ - @Deprecated - public Condition pages(String operator, Object value) { - return pageCount(operator, value); - } - public Condition pageCount(String operator, Object value) { return predicate("pc", operator, value); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java deleted file mode 100644 index 5a4ea9df..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/LayerBuilder.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.cloudinary.transformation; - -/** - * @deprecated Use {@link Layer} instead - */ -public class LayerBuilder extends Layer { -} diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java deleted file mode 100644 index 22a78625..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayerBuilder.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.cloudinary.transformation; - -/** - * @deprecated Use {@link SubtitlesLayer} instead - */ -public class SubtitlesLayerBuilder extends SubtitlesLayer { -} diff --git a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java b/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java deleted file mode 100644 index 0db485ce..00000000 --- a/cloudinary-core/src/main/java/com/cloudinary/transformation/TextLayerBuilder.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.cloudinary.transformation; - -/** - * @deprecated Use {@link TextLayer} instead - */ -public class TextLayerBuilder extends TextLayer { -} diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java index 0721acae..9e436183 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -146,6 +146,7 @@ public void testSecureDistibution() { public void testSecureAkamai() { // should default to akamai if secure is given with private_cdn and no // secure_distribution + cloudinary.config.secure = true; cloudinary.config.privateCdn = true; String result = cloudinary.url().generate("test"); assertEquals("https://test123-res.cloudinary.com/image/upload/test", result); @@ -155,6 +156,7 @@ public void testSecureAkamai() { public void testSecureNonAkamai() { // should not add cloud_name if private_cdn and secure non akamai // secure_distribution + cloudinary.config.secure = true; cloudinary.config.privateCdn = true; cloudinary.config.secureDistribution = "something.cloudfront.net"; String result = cloudinary.url().generate("test"); @@ -1209,10 +1211,9 @@ public void videoTagWithAuthTokenTest() { .type("upload") .authToken(new AuthToken("123456").duration(300)) .signed(true) -// .videoTag("sample", Cloudinary.asMap( -// "controls", true, -// "loop", true) - .videoTag("sample", asMap("controls", true, + .secure(true) + .videoTag("sample", ObjectUtils.asMap( + "controls", true, "loop", true) ); assert(actualTag.contains("cld_token")); diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java index d3738278..2de25e3b 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java @@ -72,15 +72,4 @@ public String getExtraClasses() { public void setExtraClasses(String extraClasses) { this.extraClasses = extraClasses; } - - @Deprecated - public void setPublicId(String src) { - this.src = src; - } - - @Deprecated - public String getPublicId() { - return src; - } - } \ No newline at end of file From 178559ba3ea4a7afa2cc18aa3df17dca41ead86b Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:13:04 +0300 Subject: [PATCH 16/40] Add derived next cursor support --- .../src/main/java/com/cloudinary/Api.java | 2 +- .../com/cloudinary/test/AbstractApiTest.java | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 2899327d..19cd61d8 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -200,7 +200,7 @@ public ApiResponse resource(String public_id, Map options) throws Exception { ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type, public_id), ObjectUtils.only(options, "exif", "colors", "faces", "coordinates", "image_metadata", "pages", "phash", "max_results", "quality_analysis", "cinemagraph_analysis", - "accessibility_analysis", "versions", "media_metadata"), options); + "accessibility_analysis", "versions", "media_metadata", "derived_next_cursor"), options); return response; } diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index dd3802c9..ad89074a 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -1305,4 +1305,29 @@ public void testDeleteBackedupAsset() throws Exception { assertEquals(deletedVersionIds.get(0), firstAssetVersion); } } + + @Test + public void testAllowDerivedNextCursor() throws Exception { + String publicId = "allowderivednextcursor_" + SUFFIX; + Map options = ObjectUtils.asMap("public_id", publicId, "eager", Arrays.asList( + new Transformation().width(100), + new Transformation().width(101), + new Transformation().width(102) + )); + + try { + cloudinary.uploader().upload(SRC_TEST_IMAGE, options); + ApiResponse res = api.resource(publicId, Collections.singletonMap("max_results", 1)); + String derivedNextCursor = res.get("derived_next_cursor").toString(); + assertNotNull(derivedNextCursor); + + ApiResponse res2 = api.resource(publicId, ObjectUtils.asMap("derived_next_cursor", derivedNextCursor, "max_results", 1)); + String derivedNextCursor2 = res2.get("derived_next_cursor").toString(); + assertNotNull(derivedNextCursor2); + + assertNotEquals(derivedNextCursor, derivedNextCursor2); + } finally { + cloudinary.uploader().destroy(publicId, Collections.singletonMap("invalidate", true)); + } + } } From 5e77cc58a0da6c8fd05c4b13ac7c6ab14fa7c350 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:04:01 +0300 Subject: [PATCH 17/40] Update http client --- .travis.yml | 5 +- .../src/main/java/com/cloudinary/Api.java | 44 +++- .../main/java/com/cloudinary/Cloudinary.java | 10 +- .../com/cloudinary/provisioning/Account.java | 31 ++- .../strategies/AbstractApiStrategy.java | 33 +-- cloudinary-http42/build.gradle | 114 ---------- .../com/cloudinary/http42/ApiStrategy.java | 142 ------------ .../cloudinary/http42/UploaderStrategy.java | 120 ----------- .../java/com/cloudinary/test/ApiTest.java | 23 -- .../java/com/cloudinary/test/ContextTest.java | 4 - .../com/cloudinary/http43/ApiStrategy.java | 203 ----------------- .../java/com/cloudinary/http43/ApiUtils.java | 53 ----- .../cloudinary/http43/UploaderStrategy.java | 144 ------------- .../com/cloudinary/http43/api/Response.java | 69 ------ .../com/cloudinary/test/AccountApiTest.java | 4 - .../java/com/cloudinary/test/ApiTest.java | 33 --- .../com/cloudinary/test/FoldersApiTest.java | 4 - .../java/com/cloudinary/test/SearchTest.java | 4 - .../test/StreamingProfilesApiTest.java | 7 - .../test/StructuredMetadataTest.java | 4 - .../com/cloudinary/test/UploaderTest.java | 34 --- cloudinary-http44/build.gradle | 113 ---------- .../com/cloudinary/http44/ApiStrategy.java | 204 ------------------ .../java/com/cloudinary/http44/ApiUtils.java | 53 ----- .../cloudinary/http44/UploaderStrategy.java | 145 ------------- .../com/cloudinary/http44/api/Response.java | 69 ------ .../com/cloudinary/test/AccountApiTest.java | 4 - .../java/com/cloudinary/test/ApiTest.java | 32 --- .../java/com/cloudinary/test/ContextTest.java | 5 - .../com/cloudinary/test/FoldersApiTest.java | 4 - .../java/com/cloudinary/test/SearchTest.java | 4 - .../test/StreamingProfilesApiTest.java | 7 - .../test/StructuredMetadataTest.java | 4 - .../com/cloudinary/test/UploaderTest.java | 33 --- cloudinary-http45/build.gradle | 113 ---------- .../com/cloudinary/http45/ApiStrategy.java | 201 ----------------- .../java/com/cloudinary/http45/ApiUtils.java | 53 ----- .../cloudinary/http45/UploaderStrategy.java | 141 ------------ .../com/cloudinary/http45/api/Response.java | 69 ------ .../com/cloudinary/test/AccountApiTest.java | 4 - .../java/com/cloudinary/test/ApiTest.java | 32 --- .../java/com/cloudinary/test/ContextTest.java | 5 - .../com/cloudinary/test/FoldersApiTest.java | 4 - .../java/com/cloudinary/test/SearchTest.java | 4 - .../test/StreamingProfilesApiTest.java | 4 - .../test/StructuredMetadataTest.java | 4 - .../com/cloudinary/test/UploaderTest.java | 34 --- .../build.gradle | 8 +- .../com/cloudinary/http5/ApiStrategy.java | 163 ++++++++++++++ .../java/com/cloudinary/http5/ApiUtils.java | 71 ++++++ .../cloudinary/http5/UploaderStrategy.java | 155 +++++++++++++ .../com/cloudinary/http5}/api/Response.java | 37 ++-- .../com/cloudinary/test/AccountApiTest.java | 0 .../java/com/cloudinary/test/ApiTest.java | 45 ++++ .../java/com/cloudinary/test/ContextTest.java | 0 .../com/cloudinary/test/FoldersApiTest.java | 0 .../java/com/cloudinary/test/SearchTest.java | 0 .../test/StreamingProfilesApiTest.java | 0 .../test/StructuredMetadataTest.java | 0 .../com/cloudinary/test/UploaderTest.java | 2 +- samples/photo_album_gae/pom.xml | 4 +- settings.gradle | 6 +- 62 files changed, 536 insertions(+), 2385 deletions(-) delete mode 100644 cloudinary-http42/build.gradle delete mode 100644 cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java delete mode 100644 cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java delete mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java delete mode 100644 cloudinary-http42/src/test/java/com/cloudinary/test/ContextTest.java delete mode 100644 cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java delete mode 100644 cloudinary-http43/src/main/java/com/cloudinary/http43/ApiUtils.java delete mode 100644 cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java delete mode 100644 cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java delete mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/AccountApiTest.java delete mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java delete mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/FoldersApiTest.java delete mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/SearchTest.java delete mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java delete mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/StructuredMetadataTest.java delete mode 100644 cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java delete mode 100644 cloudinary-http44/build.gradle delete mode 100644 cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java delete mode 100644 cloudinary-http44/src/main/java/com/cloudinary/http44/ApiUtils.java delete mode 100644 cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java delete mode 100644 cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java delete mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/AccountApiTest.java delete mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java delete mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/ContextTest.java delete mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/FoldersApiTest.java delete mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/SearchTest.java delete mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java delete mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/StructuredMetadataTest.java delete mode 100644 cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java delete mode 100644 cloudinary-http45/build.gradle delete mode 100644 cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java delete mode 100644 cloudinary-http45/src/main/java/com/cloudinary/http45/ApiUtils.java delete mode 100644 cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java delete mode 100644 cloudinary-http45/src/main/java/com/cloudinary/http45/api/Response.java delete mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/AccountApiTest.java delete mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/ApiTest.java delete mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/ContextTest.java delete mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/FoldersApiTest.java delete mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/SearchTest.java delete mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java delete mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/StructuredMetadataTest.java delete mode 100644 cloudinary-http45/src/test/java/com/cloudinary/test/UploaderTest.java rename {cloudinary-http43 => cloudinary-http5}/build.gradle (92%) create mode 100644 cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java create mode 100644 cloudinary-http5/src/main/java/com/cloudinary/http5/ApiUtils.java create mode 100644 cloudinary-http5/src/main/java/com/cloudinary/http5/UploaderStrategy.java rename {cloudinary-http42/src/main/java/com/cloudinary/http42 => cloudinary-http5/src/main/java/com/cloudinary/http5}/api/Response.java (72%) rename {cloudinary-http42 => cloudinary-http5}/src/test/java/com/cloudinary/test/AccountApiTest.java (100%) create mode 100644 cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java rename {cloudinary-http43 => cloudinary-http5}/src/test/java/com/cloudinary/test/ContextTest.java (100%) rename {cloudinary-http42 => cloudinary-http5}/src/test/java/com/cloudinary/test/FoldersApiTest.java (100%) rename {cloudinary-http42 => cloudinary-http5}/src/test/java/com/cloudinary/test/SearchTest.java (100%) rename {cloudinary-http42 => cloudinary-http5}/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java (100%) rename {cloudinary-http42 => cloudinary-http5}/src/test/java/com/cloudinary/test/StructuredMetadataTest.java (100%) rename {cloudinary-http42 => cloudinary-http5}/src/test/java/com/cloudinary/test/UploaderTest.java (97%) diff --git a/.travis.yml b/.travis.yml index 510d55ae..8a1fec22 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,10 +18,7 @@ jdk: env: - MODULE=core - - MODULE=http42 - - MODULE=http43 - - MODULE=http44 - - MODULE=http45 + - MODULE=http5 branches: except: diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 19cd61d8..9dbacde9 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -9,6 +9,7 @@ import com.cloudinary.metadata.MetadataDataSource; import com.cloudinary.metadata.MetadataRule; import com.cloudinary.strategies.AbstractApiStrategy; +import com.cloudinary.utils.Base64Coder; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; import org.cloudinary.json.JSONArray; @@ -40,7 +41,19 @@ public enum HttpMethod {GET, POST, PUT, DELETE;} private AbstractApiStrategy strategy; protected ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - return this.strategy.callApi(method, uri, params, options); + if (options == null) + options = ObjectUtils.emptyMap(); + + String apiKey = ObjectUtils.asString(options.get("api_key"), this.cloudinary.config.apiKey); + String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.cloudinary.config.apiSecret); + String oauthToken = ObjectUtils.asString(options.get("oauth_token"), this.cloudinary.config.oauthToken); + + validateAuthorization(apiKey, apiSecret, oauthToken); + + + String authorizationHeader = getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken); + String apiUrl = createApiUrl(uri, options); + return this.strategy.callApi(method, apiUrl, params, options, authorizationHeader); } public Api(Cloudinary cloudinary, AbstractApiStrategy strategy) { @@ -871,4 +884,33 @@ public ApiResponse deleteBackedUpAssets(String assetId, String[] versionIds, Map } return result; } + + protected void validateAuthorization(String apiKey, String apiSecret, String oauthToken) { + if (oauthToken == null) { + if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); + if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); + } + } + + protected String getAuthorizationHeaderValue(String apiKey, String apiSecret, String oauthToken) { + if (oauthToken != null){ + return "Bearer " + oauthToken; + } else { + return "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret); + } + } + + protected String createApiUrl (Iterable uri, Map options){ + String version = ObjectUtils.asString(options.get("api_version"), "v1_1"); + String prefix = ObjectUtils.asString(options.get("upload_prefix"), ObjectUtils.asString(this.cloudinary.config.uploadPrefix, "https://api.cloudinary.com")); + String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.cloudinary.config.cloudName); + if (cloudName == null) throw new IllegalArgumentException("Must supply cloud_name"); + String apiUrl = StringUtils.join(Arrays.asList(prefix, version, cloudName), "/"); + for (String component : uri) { + component = SmartUrlEncoder.encode(component); + apiUrl = apiUrl + "/" + component; + + } + return apiUrl; + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 3f636d06..27b347ac 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -22,16 +22,10 @@ public class Cloudinary { private static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.UploaderStrategy", - "com.cloudinary.http42.UploaderStrategy", - "com.cloudinary.http43.UploaderStrategy", - "com.cloudinary.http44.UploaderStrategy", - "com.cloudinary.http45.UploaderStrategy")); + "com.cloudinary.http5.UploaderStrategy")); public static List API_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.ApiStrategy", - "com.cloudinary.http42.ApiStrategy", - "com.cloudinary.http43.ApiStrategy", - "com.cloudinary.http44.ApiStrategy", - "com.cloudinary.http45.ApiStrategy")); + "com.cloudinary.http5.ApiStrategy")); public final static String CF_SHARED_CDN = "d3jpl91pxevbkh.cloudfront.net"; public final static String OLD_AKAMAI_SHARED_CDN = "cloudinary-a.akamaihd.net"; diff --git a/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java index c149e5dc..1c545345 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java +++ b/cloudinary-core/src/main/java/com/cloudinary/provisioning/Account.java @@ -4,7 +4,10 @@ import com.cloudinary.Cloudinary; import com.cloudinary.Util; import com.cloudinary.api.ApiResponse; +import com.cloudinary.utils.Base64Coder; import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; + import java.util.*; /** @@ -76,7 +79,25 @@ private ApiResponse callAccountApi(Api.HttpMethod method, List uri, Map< } Util.clearEmpty(params); - return api.getStrategy().callAccountApi(method, uri, params, options); + + if (options == null) { + options = ObjectUtils.emptyMap(); + } + + String prefix = ObjectUtils.asString(options.get("upload_prefix"), "https://api.cloudinary.com"); + String apiKey = ObjectUtils.asString(options.get("provisioning_api_key")); + if (apiKey == null) throw new IllegalArgumentException("Must supply provisioning_api_key"); + String apiSecret = ObjectUtils.asString(options.get("provisioning_api_secret")); + if (apiSecret == null) throw new IllegalArgumentException("Must supply provisioning_api_secret"); + + String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1"), "/"); + for (String component : uri) { + apiUrl = apiUrl + "/" + component; + } + + String authorizationHeader = getAuthorizationHeaderValue(apiKey, apiSecret, null); + + return api.getStrategy().callAccountApi(method, apiUrl, params, options, authorizationHeader); } /** @@ -707,4 +728,12 @@ private Map verifyOptions(Map options) { return options; } + + protected String getAuthorizationHeaderValue(String apiKey, String apiSecret, String oauthToken) { + if (oauthToken != null){ + return "Bearer " + oauthToken; + } else { + return "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret); + } + } } diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java index 2d5a514d..8878bf2c 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -18,37 +18,8 @@ public void init(Api api) { this.api = api; } - protected String createApiUrl (Iterable uri, Map options){ - String version = ObjectUtils.asString(options.get("api_version"), "v1_1"); - String prefix = ObjectUtils.asString(options.get("upload_prefix"), ObjectUtils.asString(this.api.cloudinary.config.uploadPrefix, "https://api.cloudinary.com")); - String cloudName = ObjectUtils.asString(options.get("cloud_name"), this.api.cloudinary.config.cloudName); - if (cloudName == null) throw new IllegalArgumentException("Must supply cloud_name"); - String apiUrl = StringUtils.join(Arrays.asList(prefix, version, cloudName), "/"); - for (String component : uri) { - component = SmartUrlEncoder.encode(component); - apiUrl = apiUrl + "/" + component; - - } - return apiUrl; - } - @SuppressWarnings("rawtypes") - public abstract ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception; - - public abstract ApiResponse callAccountApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception; + public abstract ApiResponse callApi(HttpMethod method, String apiUrl, Map params, Map options, String authorizationHeader) throws Exception; - protected String getAuthorizationHeaderValue(String apiKey, String apiSecret, String oauthToken) { - if (oauthToken != null){ - return "Bearer " + oauthToken; - } else { - return "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret); - } - } - - protected void validateAuthorization(String apiKey, String apiSecret, String oauthToken) { - if (oauthToken == null) { - if (apiKey == null) throw new IllegalArgumentException("Must supply api_key"); - if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); - } - } + public abstract ApiResponse callAccountApi(HttpMethod method, String apiUrl, Map params, Map options, String authorizationHeader) throws Exception; } diff --git a/cloudinary-http42/build.gradle b/cloudinary-http42/build.gradle deleted file mode 100644 index 7c94214b..00000000 --- a/cloudinary-http42/build.gradle +++ /dev/null @@ -1,114 +0,0 @@ -plugins { - id 'java-library' - id 'signing' - id 'maven-publish' - id 'io.codearte.nexus-staging' version '0.21.1' -} - -apply from: "../java_shared.gradle" - -task ciTest( type: Test ) { - useJUnit { - excludeCategories 'com.cloudinary.test.TimeoutTest' - if (System.getProperty("CLOUDINARY_ACCOUNT_URL") == "") { - exclude '**/AccountApiTest.class' - } - } -} - -dependencies { - compile project(':cloudinary-core') - compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' - compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.2.1' - compile group: 'org.apache.httpcomponents', name: 'httpcore', version: '4.2.1' - compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.2.1' - testCompile project(':cloudinary-test-common') - testCompile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' - testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' - testCompile group: 'junit', name: 'junit', version: '4.12' -} - -if (hasProperty("ossrhPassword")) { - - signing { - sign configurations.archives - } - - nexusStaging { - packageGroup = group - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - pom { - name = 'Cloudinary Apache HTTP 4.2 Library' - packaging = 'jar' - groupId = publishGroupId - artifactId = 'cloudinary-http42' - description = publishDescription - url = githubUrl - licenses { - license { - name = licenseName - url = licenseUrl - } - } - - developers { - developer { - id = developerId - name = developerName - email = developerEmail - } - } - scm { - connection = scmConnection - developerConnection = scmDeveloperConnection - url = scmUrl - } - } - - pom.withXml { - def pomFile = file("${project.buildDir}/generated-pom.xml") - writeTo(pomFile) - def pomAscFile = signing.sign(pomFile).signatureFiles[0] - artifact(pomAscFile) { - classifier = null - extension = 'pom.asc' - } - } - - // create the signed artifacts - project.tasks.signArchives.signatureFiles.each { - artifact(it) { - def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ - if (matcher.find()) { - classifier = matcher.group(1) - } else { - classifier = null - } - extension = 'jar.asc' - } - } - } - } - - model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("$buildDir/generated-pom.xml") - } - tasks.publishMavenJavaPublicationToMavenLocal { - dependsOn project.tasks.signArchives - } - tasks.publishMavenJavaPublicationToSonatypeRepository { - dependsOn project.tasks.signArchives - } - } - } -} diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java deleted file mode 100644 index 4a90ed86..00000000 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/ApiStrategy.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.cloudinary.http42; - -import com.cloudinary.Api; -import com.cloudinary.Api.HttpMethod; -import com.cloudinary.Cloudinary; -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.exceptions.GeneralError; -import com.cloudinary.http42.api.Response; -import com.cloudinary.strategies.AbstractApiStrategy; -import com.cloudinary.utils.Base64Coder; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.*; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.cloudinary.json.JSONException; -import org.cloudinary.json.JSONObject; - -import java.io.InputStream; -import java.lang.reflect.Constructor; -import java.net.URI; -import java.util.Arrays; -import java.util.Map; - -public class ApiStrategy extends AbstractApiStrategy { - - @SuppressWarnings({"rawtypes", "unchecked"}) - public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - if (options == null) options = ObjectUtils.emptyMap(); - - String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.config.apiKey); - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); - String oauthToken = ObjectUtils.asString(options.get("oauth_token"), this.api.cloudinary.config.oauthToken); - String contentType = ObjectUtils.asString(options.get("content_type"), "urlencoded"); - int timeout = ObjectUtils.asInteger(options.get("timeout"), this.api.cloudinary.config.timeout); - validateAuthorization(apiKey, apiSecret, oauthToken); - - String apiUrl = createApiUrl(uri, options); - - return getApiResponse(method, params, apiKey, apiSecret, oauthToken, contentType, timeout, apiUrl); - } - - @Override - public ApiResponse callAccountApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - String prefix = ObjectUtils.asString(options.get("upload_prefix"), "https://api.cloudinary.com"); - String apiKey = ObjectUtils.asString(options.get("provisioning_api_key")); - if (apiKey == null) throw new IllegalArgumentException("Must supply provisioning_api_key"); - String apiSecret = ObjectUtils.asString(options.get("provisioning_api_secret")); - if (apiSecret == null) throw new IllegalArgumentException("Must supply provisioning_api_secret"); - String contentType = ObjectUtils.asString(options.get("content_type"), "urlencoded"); - int timeout = ObjectUtils.asInteger(options.get("timeout"), this.api.cloudinary.config.timeout); - - String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1"), "/"); - for (String component : uri) { - apiUrl = apiUrl + "/" + component; - } - - return getApiResponse(method, params, apiKey, apiSecret, null, contentType, timeout, apiUrl); - } - - private ApiResponse getApiResponse(HttpMethod method, Map params, String apiKey, String apiSecret, String oauthToken, String contentType, int timeout, String apiUrl) throws Exception { - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - if (!contentType.equals("json")) { - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Iterable) { - for (String single : (Iterable) param.getValue()) { - apiUrlBuilder.addParameter(param.getKey() + "[]", single); - } - } else { - apiUrlBuilder.addParameter(param.getKey(), ObjectUtils.asString(param.getValue())); - } - } - } - - ClientConnectionManager connectionManager = (ClientConnectionManager) this.api.cloudinary.config.properties.get("connectionManager"); - - DefaultHttpClient client = new DefaultHttpClient(connectionManager); - if (timeout > 0) { - HttpParams httpParams = client.getParams(); - HttpConnectionParams.setConnectionTimeout(httpParams, timeout); - HttpConnectionParams.setSoTimeout(httpParams, timeout); - } - - URI apiUri = apiUrlBuilder.build(); - HttpUriRequest request = null; - switch (method) { - case GET: - request = new HttpGet(apiUri); - break; - case PUT: - request = new HttpPut(apiUri); - break; - case POST: - request = new HttpPost(apiUri); - break; - case DELETE: - request = new HttpDelete(apiUri); - break; - } - request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken)); - request.setHeader("User-Agent", this.api.cloudinary.getUserAgent() + " ApacheHTTPComponents/4.2"); - if (contentType.equals("json")) { - JSONObject asJSON = ObjectUtils.toJSON(params); - StringEntity requestEntity = new StringEntity(asJSON.toString(), ContentType.APPLICATION_JSON); - ((HttpEntityEnclosingRequestBase) request).setEntity(requestEntity); - } - - HttpResponse response = client.execute(request); - - int code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - String responseData = StringUtils.read(responseStream); - - Class exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code); - if (code != 200 && exceptionClass == null) { - throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData); - } - Map result; - - try { - JSONObject responseJSON = new JSONObject(responseData); - result = ObjectUtils.toMap(responseJSON); - } catch (JSONException e) { - throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); - } - - if (code == 200) { - return new Response(response, result); - } else { - String message = (String) ((Map) result.get("error")).get("message"); - Constructor exceptionConstructor = exceptionClass.getConstructor(String.class); - throw exceptionConstructor.newInstance(message); - } - } - -} diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java b/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java deleted file mode 100644 index 0e38365d..00000000 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/UploaderStrategy.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.cloudinary.http42; - -import com.cloudinary.Cloudinary; -import com.cloudinary.ProgressCallback; -import com.cloudinary.Util; -import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; -import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.ClientConnectionManager; -import org.apache.http.conn.params.ConnRoutePNames; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MultipartEntity; -import org.apache.http.entity.mime.content.ByteArrayBody; -import org.apache.http.entity.mime.content.FileBody; -import org.apache.http.entity.mime.content.StringBody; -import org.apache.http.impl.client.DefaultHttpClient; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Map; - -public class UploaderStrategy extends AbstractUploaderStrategy { - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Override - public Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException { - if (progressCallback != null){ - throw new IllegalArgumentException("Progress callback is not supported"); - } - - // initialize options if passed as null - if (options == null) { - options = ObjectUtils.emptyMap(); - } - - boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - - if (requiresSigning(action, options)) { - uploader.signRequestParams(params, options); - } else { - Util.clearEmpty(params); - } - - String apiUrl = buildUploadUrl(action, options); - - ClientConnectionManager connectionManager = (ClientConnectionManager) this.uploader.cloudinary().config.properties.get("connectionManager"); - HttpClient client = new DefaultHttpClient(connectionManager); - - // If the configuration specifies a proxy then apply it to the client - if (uploader.cloudinary().config.proxyHost != null && uploader.cloudinary().config.proxyPort != 0) { - HttpHost proxy = new HttpHost(uploader.cloudinary().config.proxyHost, uploader.cloudinary().config.proxyPort); - client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); - } - - HttpPost postMethod = new HttpPost(apiUrl); - postMethod.setHeader("User-Agent", this.cloudinary().getUserAgent() + " ApacheHTTPComponents/4.2"); - - Map extraHeaders = (Map) options.get("extra_headers"); - if (extraHeaders != null) { - for (Map.Entry header : extraHeaders.entrySet()) { - postMethod.setHeader(header.getKey(), header.getValue()); - } - } - - Charset utf8 = Charset.forName("UTF-8"); - - MultipartEntity multipart = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, StandardCharsets.UTF_8); - // Remove blank parameters - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Collection) { - for (Object value : (Collection) param.getValue()) { - multipart.addPart(param.getKey() + "[]", new StringBody(ObjectUtils.asString(value), utf8)); - } - } else { - String value = param.getValue().toString(); - if (StringUtils.isNotBlank(value)) { - multipart.addPart(param.getKey(), new StringBody(value, utf8)); - } - } - } - if(file instanceof String && !(StringUtils.isRemoteUrl((String)file))){ - File _file = new File((String) file); - if (!_file.isFile() && !_file.canRead()) { - throw new IOException("File not found or unreadable: " + file); - } - file = _file; - } - - String filename = (String) options.get("filename"); - if (file instanceof File) { - multipart.addPart("file", new FileBody((File) file, filename, "application/octet-stream", null)); - } else if (file instanceof String) { - multipart.addPart("file", new StringBody((String) file, utf8)); - } else if (file instanceof byte[]) { - if (filename == null) filename = "file"; - multipart.addPart("file", new ByteArrayBody((byte[]) file, filename)); - } else if (file == null) { - // no-problem - } else { - throw new IOException("Unrecognized file parameter " + file); - } - postMethod.setEntity(multipart); - - HttpResponse response = client.execute(postMethod); - int code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - String responseData = StringUtils.read(responseStream); - - return processResponse(returnError, code, responseData); - } - -} diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java deleted file mode 100644 index b3fa3556..00000000 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ApiTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.cloudinary.test; - -import org.apache.http.conn.ConnectTimeoutException; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class ApiTest extends AbstractApiTest { - @Category(TimeoutTest.class) - @Test(expected = ConnectTimeoutException.class) - public void testTimeoutException() throws Exception { - // should allow listing resources - Map options = new HashMap(); - options.put("timeout", Integer.valueOf(1)); - - Map result = api.resources(options); - Map resource = findByAttr((List) result.get("resources"), "public_id", "api_test"); - - } -} diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/ContextTest.java b/cloudinary-http42/src/test/java/com/cloudinary/test/ContextTest.java deleted file mode 100644 index 1c126299..00000000 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/ContextTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class ContextTest extends AbstractContextTest { -} diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java deleted file mode 100644 index 0d619e11..00000000 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiStrategy.java +++ /dev/null @@ -1,203 +0,0 @@ -package com.cloudinary.http43; - -import com.cloudinary.Api; -import com.cloudinary.Api.HttpMethod; -import com.cloudinary.Cloudinary; -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.exceptions.GeneralError; -import com.cloudinary.http43.api.Response; -import com.cloudinary.utils.Base64Coder; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; -import org.apache.http.Consts; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.conn.HttpClientConnectionManager; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; -import org.cloudinary.json.JSONException; -import org.cloudinary.json.JSONObject; - -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Constructor; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import static com.cloudinary.http43.ApiUtils.prepareParams; -import static com.cloudinary.http43.ApiUtils.setTimeouts; - -public class ApiStrategy extends com.cloudinary.strategies.AbstractApiStrategy { - - private CloseableHttpClient client = null; - - @Override - public void init(Api api) { - super.init(api); - - HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(this.api.cloudinary.getUserAgent() + " ApacheHTTPComponents/4.3"); - - // If the configuration specifies a proxy then apply it to the client - if (api.cloudinary.config.proxyHost != null && api.cloudinary.config.proxyPort != 0) { - HttpHost proxy = new HttpHost(api.cloudinary.config.proxyHost, api.cloudinary.config.proxyPort); - clientBuilder.setProxy(proxy); - } - - HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) api.cloudinary.config.properties.get("connectionManager"); - if (connectionManager != null) { - clientBuilder.setConnectionManager(connectionManager); - } - - int timeout = this.api.cloudinary.config.timeout; - if (timeout > 0) { - RequestConfig config = RequestConfig.custom() - .setSocketTimeout(timeout * 1000) - .setConnectTimeout(timeout * 1000) - .build(); - clientBuilder.setDefaultRequestConfig(config); - } - - this.client = clientBuilder.build(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - - String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.config.apiKey); - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); - String oauthToken = ObjectUtils.asString(options.get("oauth_token"), this.api.cloudinary.config.oauthToken); - - validateAuthorization(apiKey, apiSecret, oauthToken); - - String apiUrl = createApiUrl(uri, options); - HttpUriRequest request = prepareRequest(method, apiUrl, params, options); - - request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken)); - - return getApiResponse(request); - } - - private ApiResponse getApiResponse(HttpUriRequest request) throws Exception { - String responseData = null; - int code = 0; - CloseableHttpResponse response = client.execute(request); - try { - code = response.getStatusLine().getStatusCode(); - final HttpEntity entity = response.getEntity(); - responseData = StringUtils.read(entity.getContent()); - EntityUtils.consume(entity); - } finally { - response.close(); - } - - Class exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code); - if (code != 200 && exceptionClass == null) { - throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData); - } - Map result; - - try { - JSONObject responseJSON = new JSONObject(responseData); - result = ObjectUtils.toMap(responseJSON); - } catch (JSONException e) { - throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); - } - - if (code == 200) { - return new Response(response, result); - } else { - String message = (String) ((Map) result.get("error")).get("message"); - Constructor exceptionConstructor = exceptionClass.getConstructor(String.class); - throw exceptionConstructor.newInstance(message); - } - } - - @Override - public ApiResponse callAccountApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - - String prefix = ObjectUtils.asString(options.get("upload_prefix"), "https://api.cloudinary.com"); - String apiKey = ObjectUtils.asString(options.get("provisioning_api_key")); - if (apiKey == null) throw new IllegalArgumentException("Must supply provisioning_api_key"); - String apiSecret = ObjectUtils.asString(options.get("provisioning_api_secret")); - if (apiSecret == null) throw new IllegalArgumentException("Must supply provisioning_api_secret"); - - String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1"), "/"); - for (String component : uri) { - apiUrl = apiUrl + "/" + component; - } - - HttpUriRequest request = prepareRequest(method, apiUrl, params, options); - - request.setHeader("Authorization", "Basic " + Base64Coder.encodeString(apiKey + ":" + apiSecret)); - - return getApiResponse(request); - } - - /** - * Prepare a request with the URL and parameters based on the HTTP method used - * - * @param method the HTTP method: GET, PUT, POST, DELETE - * @param apiUrl the cloudinary API URI - * @param params the parameters to pass to the server - * @return an HTTP request - * @throws URISyntaxException - * @throws UnsupportedEncodingException - */ - private HttpUriRequest prepareRequest(HttpMethod method, String apiUrl, Map params, Map options) throws URISyntaxException, UnsupportedEncodingException { - URI apiUri; - HttpRequestBase request; - - String contentType = ObjectUtils.asString(options.get("content_type"), "urlencoded"); - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - HashMap unboxedParams = new HashMap(params); - - if (method == HttpMethod.GET) { - apiUrlBuilder.setParameters(prepareParams(params)); - apiUri = apiUrlBuilder.build(); - request = new HttpGet(apiUri); - } else { - apiUri = apiUrlBuilder.build(); - switch (method) { - case PUT: - request = new HttpPut(apiUri); - break; - case DELETE: //uses HttpPost instead of HttpDelete - unboxedParams.put("_method","delete"); - //continue with POST - case POST: - request = new HttpPost(apiUri); - break; - default: - throw new IllegalArgumentException("Unknown HTTP method"); - } - if (contentType.equals("json")) { - JSONObject asJSON = ObjectUtils.toJSON(unboxedParams); - StringEntity requestEntity = new StringEntity(asJSON.toString(), ContentType.APPLICATION_JSON); - ((HttpEntityEnclosingRequestBase) request).setEntity(requestEntity); - } else { - ((HttpEntityEnclosingRequestBase) request).setEntity(new UrlEncodedFormEntity(prepareParams(unboxedParams), Consts.UTF_8)); - } - } - - setTimeouts(request, options); - return request; - } - - -} diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiUtils.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiUtils.java deleted file mode 100644 index 0356934b..00000000 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/ApiUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.cloudinary.http43; - -import com.cloudinary.utils.ObjectUtils; -import org.apache.http.NameValuePair; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.message.BasicNameValuePair; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class ApiUtils { - - public static void setTimeouts(HttpRequestBase request, Map options) { - RequestConfig config= request.getConfig(); - final RequestConfig.Builder builder; - if (config != null) { - builder = RequestConfig.copy(config); - } else { - builder = RequestConfig.custom(); - } - Integer timeout = (Integer) options.get("timeout"); - if(timeout != null) { - builder.setSocketTimeout(timeout); - } - Integer connectionRequestTimeout = (Integer) options.get("connection_request_timeout"); - if(connectionRequestTimeout != null) { - builder.setConnectionRequestTimeout(connectionRequestTimeout); - } - Integer connectTimeout = (Integer) options.get("connect_timeout"); - if(connectTimeout != null) { - builder.setConnectTimeout(connectTimeout); - } - request.setConfig(builder.build()); - } - - static List prepareParams(Map params) { - List requestParams = new ArrayList(params.size()); - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Iterable) { - for (Object single : (Iterable) param.getValue()) { - requestParams.add(new BasicNameValuePair(param.getKey() + "[]", ObjectUtils.asString(single))); - } - } else { - requestParams.add(new BasicNameValuePair(param.getKey(), ObjectUtils.asString(param.getValue()))); - } - } - - - return requestParams; - } -} diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java deleted file mode 100644 index 88ce4b90..00000000 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/UploaderStrategy.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.cloudinary.http43; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Map; - -import com.cloudinary.ProgressCallback; -import org.apache.http.HttpHost; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.HttpClientConnectionManager; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MIME; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.cloudinary.json.JSONException; -import org.cloudinary.json.JSONObject; - -import com.cloudinary.Cloudinary; -import com.cloudinary.Uploader; -import com.cloudinary.Util; -import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; - -public class UploaderStrategy extends AbstractUploaderStrategy { - - private CloseableHttpClient client = null; - - @Override - public void init(Uploader uploader) { - super.init(uploader); - - HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(this.cloudinary().getUserAgent() + " ApacheHTTPComponents/4.3"); - - // If the configuration specifies a proxy then apply it to the client - if (cloudinary().config.proxyHost != null && cloudinary().config.proxyPort != 0) { - HttpHost proxy = new HttpHost(cloudinary().config.proxyHost, cloudinary().config.proxyPort); - clientBuilder.setProxy(proxy); - } - - HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) cloudinary().config.properties.get("connectionManager"); - if (connectionManager != null) { - clientBuilder.setConnectionManager(connectionManager); - } - - this.client = clientBuilder.build(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Override - public Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException { - if (progressCallback != null){ - throw new IllegalArgumentException("Progress callback is not supported"); - } - - // initialize options if passed as null - if (options == null) { - options = ObjectUtils.emptyMap(); - } - - boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - - if (requiresSigning(action, options)) { - uploader.signRequestParams(params, options); - } else { - Util.clearEmpty(params); - } - - String apiUrl = buildUploadUrl(action, options); - - HttpPost postMethod = new HttpPost(apiUrl); - ApiUtils.setTimeouts(postMethod, options); - - Map extraHeaders = (Map) options.get("extra_headers"); - if (extraHeaders != null) { - for (Map.Entry header : extraHeaders.entrySet()) { - postMethod.setHeader(header.getKey(), header.getValue()); - } - } - - MultipartEntityBuilder multipart = MultipartEntityBuilder.create(); - multipart.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); - multipart.setCharset(StandardCharsets.UTF_8); - ContentType contentType = ContentType.MULTIPART_FORM_DATA.withCharset(MIME.UTF8_CHARSET); - // Remove blank parameters - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Collection) { - for (Object value : (Collection) param.getValue()) { - multipart.addTextBody(param.getKey() + "[]", ObjectUtils.asString(value), contentType); - } - } else { - String value = param.getValue().toString(); - if (StringUtils.isNotBlank(value)) { - multipart.addTextBody(param.getKey(), value, contentType); - } - } - } - - if(file instanceof String && !(StringUtils.isRemoteUrl((String)file))){ - File _file = new File((String) file); - if (!_file.isFile() && !_file.canRead()) { - throw new IOException("File not found or unreadable: " + file); - } - file = _file; - } - String filename = (String) options.get("filename"); - if (file instanceof File) { - if (filename == null) filename = ((File) file).getName(); - multipart.addBinaryBody("file", (File) file, ContentType.APPLICATION_OCTET_STREAM, filename); - } else if (file instanceof String) { - multipart.addTextBody("file", (String) file, contentType); - } else if (file instanceof byte[]) { - if (filename == null) filename = "file"; - multipart.addBinaryBody("file", (byte[]) file, ContentType.APPLICATION_OCTET_STREAM, filename); - } else if (file == null) { - // no-problem - } else { - throw new IOException("Unrecognized file parameter " + file); - } - postMethod.setEntity(multipart.build()); - - String responseData = null; - int code = 0; - CloseableHttpResponse response = client.execute(postMethod); - try { - code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - responseData = StringUtils.read(responseStream); - } finally { - response.close(); - } - - return processResponse(returnError, code, responseData); - } - -} diff --git a/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java b/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java deleted file mode 100644 index 51cd9219..00000000 --- a/cloudinary-http43/src/main/java/com/cloudinary/http43/api/Response.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.cloudinary.http43.api; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.http.Header; -import org.apache.http.HttpResponse; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.RateLimit; -import com.cloudinary.utils.StringUtils; - -@SuppressWarnings("rawtypes") -public class Response extends HashMap implements ApiResponse { - private static final long serialVersionUID = -5458609797599845837L; - private HttpResponse response = null; - - @SuppressWarnings("unchecked") - public Response(HttpResponse response, Map result) { - super(result); - this.response = response; - } - - public HttpResponse getRawHttpResponse() { - return this.response; - } - - private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-FEATURE(\\w*)RATELIMIT(-LIMIT|-RESET|-REMAINING)", Pattern.CASE_INSENSITIVE); - private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat(RFC1123_PATTERN, Locale.ENGLISH); - - public Map rateLimits() throws java.text.ParseException { - Header[] headers = this.response.getAllHeaders(); - Map limits = new HashMap(); - for (Header header : headers) { - Matcher m = RATE_LIMIT_REGEX.matcher(header.getName()); - if (m.matches()) { - String limitName = "Api"; - RateLimit limit = null; - if (!StringUtils.isEmpty(m.group(1))) { - limitName = m.group(1); - } - limit = limits.get(limitName); - if (limit == null) { - limit = new RateLimit(); - } - if (m.group(2).equalsIgnoreCase("-limit")) { - limit.setLimit(Long.parseLong(header.getValue())); - } else if (m.group(2).equalsIgnoreCase("-remaining")) { - limit.setRemaining(Long.parseLong(header.getValue())); - } else if (m.group(2).equalsIgnoreCase("-reset")) { - limit.setReset(RFC1123.parse(header.getValue())); - } - limits.put(limitName, limit); - } - } - return limits; - } - - public RateLimit apiRateLimit() throws java.text.ParseException { - return rateLimits().get("Api"); - } -} diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/AccountApiTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/AccountApiTest.java deleted file mode 100644 index 573a12e5..00000000 --- a/cloudinary-http43/src/test/java/com/cloudinary/test/AccountApiTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class AccountApiTest extends AbstractAccountApiTest { -} diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java deleted file mode 100644 index 530b644f..00000000 --- a/cloudinary-http43/src/test/java/com/cloudinary/test/ApiTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.cloudinary.test; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.utils.ObjectUtils; -import org.apache.http.conn.ConnectTimeoutException; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import java.net.SocketTimeoutException; -import java.util.Map; - -public class ApiTest extends AbstractApiTest { - @Category(TimeoutTest.class) - @Test(expected = ConnectTimeoutException.class) - public void testConnectTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "connect_timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } - - @Category(TimeoutTest.class) - @Test(expected = SocketTimeoutException.class) - public void testTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } - -} diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/FoldersApiTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/FoldersApiTest.java deleted file mode 100644 index 971bcf39..00000000 --- a/cloudinary-http43/src/test/java/com/cloudinary/test/FoldersApiTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class FoldersApiTest extends AbstractFoldersApiTest { -} diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/SearchTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/SearchTest.java deleted file mode 100644 index 16a4708c..00000000 --- a/cloudinary-http43/src/test/java/com/cloudinary/test/SearchTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class SearchTest extends AbstractSearchTest { -} diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java deleted file mode 100644 index 4e763579..00000000 --- a/cloudinary-http43/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.cloudinary.test; - -/** - * Created by amir on 25/10/2016. - */ -public class StreamingProfilesApiTest extends AbstractStreamingProfilesApiTest { -} diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/StructuredMetadataTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/StructuredMetadataTest.java deleted file mode 100644 index 900da239..00000000 --- a/cloudinary-http43/src/test/java/com/cloudinary/test/StructuredMetadataTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class StructuredMetadataTest extends AbstractStructuredMetadataTest { -} \ No newline at end of file diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java deleted file mode 100644 index efbf9190..00000000 --- a/cloudinary-http43/src/test/java/com/cloudinary/test/UploaderTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.cloudinary.test; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.utils.ObjectUtils; -import org.apache.http.conn.ConnectTimeoutException; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import java.net.SocketTimeoutException; -import java.util.Map; - -public class UploaderTest extends AbstractUploaderTest { - - @Category(TimeoutTest.class) - @Test(expected = ConnectTimeoutException.class) - public void testConnectTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "connect_timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } - - @Category(TimeoutTest.class) - @Test(expected = SocketTimeoutException.class) - public void testTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } - -} \ No newline at end of file diff --git a/cloudinary-http44/build.gradle b/cloudinary-http44/build.gradle deleted file mode 100644 index a4c51ed8..00000000 --- a/cloudinary-http44/build.gradle +++ /dev/null @@ -1,113 +0,0 @@ -plugins { - id 'java-library' - id 'signing' - id 'maven-publish' - id 'io.codearte.nexus-staging' version '0.21.1' -} - -apply from: "../java_shared.gradle" - -task ciTest( type: Test ) { - useJUnit { - excludeCategories 'com.cloudinary.test.TimeoutTest' - if (System.getProperty("CLOUDINARY_ACCOUNT_URL") == "") { - exclude '**/AccountApiTest.class' - } - } -} - -dependencies { - compile project(':cloudinary-core') - compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' - compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.4' - compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.4' - testCompile project(':cloudinary-test-common') - testCompile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' - testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' - testCompile group: 'junit', name: 'junit', version: '4.12' -} - -if (hasProperty("ossrhPassword")) { - - signing { - sign configurations.archives - } - - nexusStaging { - packageGroup = group - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - pom { - name = 'Cloudinary Apache HTTP 4.4 Library' - packaging = 'jar' - groupId = publishGroupId - artifactId = 'cloudinary-http44' - description = publishDescription - url = githubUrl - licenses { - license { - name = licenseName - url = licenseUrl - } - } - - developers { - developer { - id = developerId - name = developerName - email = developerEmail - } - } - scm { - connection = scmConnection - developerConnection = scmDeveloperConnection - url = scmUrl - } - } - - pom.withXml { - def pomFile = file("${project.buildDir}/generated-pom.xml") - writeTo(pomFile) - def pomAscFile = signing.sign(pomFile).signatureFiles[0] - artifact(pomAscFile) { - classifier = null - extension = 'pom.asc' - } - } - - // create the signed artifacts - project.tasks.signArchives.signatureFiles.each { - artifact(it) { - def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ - if (matcher.find()) { - classifier = matcher.group(1) - } else { - classifier = null - } - extension = 'jar.asc' - } - } - } - } - - model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("$buildDir/generated-pom.xml") - } - tasks.publishMavenJavaPublicationToMavenLocal { - dependsOn project.tasks.signArchives - } - tasks.publishMavenJavaPublicationToSonatypeRepository { - dependsOn project.tasks.signArchives - } - } - } -} diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java deleted file mode 100644 index b74f1dd7..00000000 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiStrategy.java +++ /dev/null @@ -1,204 +0,0 @@ -package com.cloudinary.http44; - -import com.cloudinary.Api; -import com.cloudinary.Api.HttpMethod; -import com.cloudinary.Cloudinary; -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.exceptions.GeneralError; -import com.cloudinary.http44.api.Response; -import com.cloudinary.utils.Base64Coder; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; -import org.apache.http.Consts; -import org.apache.http.HttpEntity; -import org.apache.http.HttpHost; -import org.apache.http.NameValuePair; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.conn.HttpClientConnectionManager; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.util.EntityUtils; -import org.cloudinary.json.JSONException; -import org.cloudinary.json.JSONObject; - -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Constructor; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static com.cloudinary.http44.ApiUtils.prepareParams; -import static com.cloudinary.http44.ApiUtils.setTimeouts; - -public class ApiStrategy extends com.cloudinary.strategies.AbstractApiStrategy { - - private CloseableHttpClient client = null; - - @Override - public void init(Api api) { - super.init(api); - - HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(this.api.cloudinary.getUserAgent() + " ApacheHTTPComponents/4.4"); - - // If the configuration specifies a proxy then apply it to the client - if (api.cloudinary.config.proxyHost != null && api.cloudinary.config.proxyPort != 0) { - HttpHost proxy = new HttpHost(api.cloudinary.config.proxyHost, api.cloudinary.config.proxyPort); - clientBuilder.setProxy(proxy); - } - - HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) api.cloudinary.config.properties.get("connectionManager"); - if (connectionManager != null) { - clientBuilder.setConnectionManager(connectionManager); - } - - int timeout = this.api.cloudinary.config.timeout; - if (timeout > 0) { - RequestConfig config = RequestConfig.custom() - .setSocketTimeout(timeout * 1000) - .setConnectTimeout(timeout * 1000) - .build(); - clientBuilder.setDefaultRequestConfig(config); - } - - this.client = clientBuilder.build(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - - String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.config.apiKey); - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); - String oauthToken = ObjectUtils.asString(options.get("oauth_token"), this.api.cloudinary.config.oauthToken); - - validateAuthorization(apiKey, apiSecret, oauthToken); - - String apiUrl = createApiUrl(uri, options); - HttpUriRequest request = prepareRequest(method, apiUrl, params, options); - - request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken)); - - return getApiResponse(request); - } - - private ApiResponse getApiResponse(HttpUriRequest request) throws Exception { - String responseData = null; - int code = 0; - CloseableHttpResponse response = client.execute(request); - try { - code = response.getStatusLine().getStatusCode(); - final HttpEntity entity = response.getEntity(); - responseData = StringUtils.read(entity.getContent()); - EntityUtils.consume(entity); - } finally { - response.close(); - } - - Class exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code); - if (code != 200 && exceptionClass == null) { - throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData); - } - Map result; - - try { - JSONObject responseJSON = new JSONObject(responseData); - result = ObjectUtils.toMap(responseJSON); - } catch (JSONException e) { - throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); - } - - if (code == 200) { - return new Response(response, result); - } else { - String message = (String) ((Map) result.get("error")).get("message"); - Constructor exceptionConstructor = exceptionClass.getConstructor(String.class); - throw exceptionConstructor.newInstance(message); - } - } - - @Override - public ApiResponse callAccountApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - - String prefix = ObjectUtils.asString(options.get("upload_prefix"), "https://api.cloudinary.com"); - String apiKey = ObjectUtils.asString(options.get("provisioning_api_key")); - if (apiKey == null) throw new IllegalArgumentException("Must supply provisioning_api_key"); - String apiSecret = ObjectUtils.asString(options.get("provisioning_api_secret")); - if (apiSecret == null) throw new IllegalArgumentException("Must supply provisioning_api_secret"); - - String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1"), "/"); - for (String component : uri) { - apiUrl = apiUrl + "/" + component; - } - - HttpUriRequest request = prepareRequest(method, apiUrl, params, options); - - request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, null)); - - return getApiResponse(request); - } - - /** - * Prepare a request with the URL and parameters based on the HTTP method used - * - * @param method the HTTP method: GET, PUT, POST, DELETE - * @param apiUrl the cloudinary API URI - * @param params the parameters to pass to the server - * @return an HTTP request - * @throws URISyntaxException - * @throws UnsupportedEncodingException - */ - private HttpUriRequest prepareRequest(HttpMethod method, String apiUrl, Map params, Map options) throws URISyntaxException, UnsupportedEncodingException { - URI apiUri; - HttpRequestBase request; - - String contentType = ObjectUtils.asString(options.get("content_type"), "urlencoded"); - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - List urlEncodedParams = prepareParams(params); - - if (method == HttpMethod.GET) { - apiUrlBuilder.setParameters(prepareParams(params)); - apiUri = apiUrlBuilder.build(); - request = new HttpGet(apiUri); - } else { - Map paramsCopy = new HashMap((Map) params); - apiUri = apiUrlBuilder.build(); - switch (method) { - case PUT: - request = new HttpPut(apiUri); - break; - case DELETE: //uses HttpPost instead of HttpDelete - paramsCopy.put("_method", "delete"); - //continue with POST - case POST: - request = new HttpPost(apiUri); - break; - default: - throw new IllegalArgumentException("Unknown HTTP method"); - } - if (contentType.equals("json")) { - JSONObject asJSON = ObjectUtils.toJSON(paramsCopy); - StringEntity requestEntity = new StringEntity(asJSON.toString(), ContentType.APPLICATION_JSON); - ((HttpEntityEnclosingRequestBase) request).setEntity(requestEntity); - } else { - ((HttpEntityEnclosingRequestBase) request).setEntity(new UrlEncodedFormEntity(prepareParams(paramsCopy), Consts.UTF_8)); - } - } - - setTimeouts(request, options); - return request; - } -} diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiUtils.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiUtils.java deleted file mode 100644 index 1a3da83f..00000000 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/ApiUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.cloudinary.http44; - -import com.cloudinary.utils.ObjectUtils; -import org.apache.http.NameValuePair; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.message.BasicNameValuePair; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class ApiUtils { - - public static void setTimeouts(HttpRequestBase request, Map options) { - RequestConfig config= request.getConfig(); - final RequestConfig.Builder builder; - if (config != null) { - builder = RequestConfig.copy(config); - } else { - builder = RequestConfig.custom(); - } - Integer timeout = (Integer) options.get("timeout"); - if(timeout != null) { - builder.setSocketTimeout(timeout); - } - Integer connectionRequestTimeout = (Integer) options.get("connection_request_timeout"); - if(connectionRequestTimeout != null) { - builder.setConnectionRequestTimeout(connectionRequestTimeout); - } - Integer connectTimeout = (Integer) options.get("connect_timeout"); - if(connectTimeout != null) { - builder.setConnectTimeout(connectTimeout); - } - request.setConfig(builder.build()); - } - - static List prepareParams(Map params) { - List requestParams = new ArrayList(params.size()); - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Iterable) { - for (Object single : (Iterable) param.getValue()) { - requestParams.add(new BasicNameValuePair(param.getKey() + "[]", ObjectUtils.asString(single))); - } - } else { - requestParams.add(new BasicNameValuePair(param.getKey(), ObjectUtils.asString(param.getValue()))); - } - } - - - return requestParams; - } -} diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java deleted file mode 100644 index 3afc8bce..00000000 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/UploaderStrategy.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.cloudinary.http44; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Map; - -import com.cloudinary.ProgressCallback; -import org.apache.http.HttpHost; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.HttpClientConnectionManager; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MIME; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.cloudinary.json.JSONException; -import org.cloudinary.json.JSONObject; - -import com.cloudinary.Cloudinary; -import com.cloudinary.Uploader; -import com.cloudinary.Util; -import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; - -public class UploaderStrategy extends AbstractUploaderStrategy { - - private CloseableHttpClient client = null; - - @Override - public void init(Uploader uploader) { - super.init(uploader); - - HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(this.cloudinary().getUserAgent() + " ApacheHTTPComponents/4.4"); - - // If the configuration specifies a proxy then apply it to the client - if (cloudinary().config.proxyHost != null && cloudinary().config.proxyPort != 0) { - HttpHost proxy = new HttpHost(cloudinary().config.proxyHost, cloudinary().config.proxyPort); - clientBuilder.setProxy(proxy); - } - - HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) cloudinary().config.properties.get("connectionManager"); - if (connectionManager != null) { - clientBuilder.setConnectionManager(connectionManager); - } - - this.client = clientBuilder.build(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Override - public Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException { - if (progressCallback != null){ - throw new IllegalArgumentException("Progress callback is not supported"); - } - - // initialize options if passed as null - if (options == null) { - options = ObjectUtils.emptyMap(); - } - - boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - - if (requiresSigning(action, options)) { - uploader.signRequestParams(params, options); - } else { - Util.clearEmpty(params); - } - - String apiUrl = buildUploadUrl(action, options); - - HttpPost postMethod = new HttpPost(apiUrl); - ApiUtils.setTimeouts(postMethod, options); - - Map extraHeaders = (Map) options.get("extra_headers"); - if (extraHeaders != null) { - for (Map.Entry header : extraHeaders.entrySet()) { - postMethod.setHeader(header.getKey(), header.getValue()); - } - } - - MultipartEntityBuilder multipart = MultipartEntityBuilder.create(); - multipart.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); - multipart.setCharset(StandardCharsets.UTF_8); - ContentType contentType = ContentType.MULTIPART_FORM_DATA.withCharset(MIME.UTF8_CHARSET); - // Remove blank parameters - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Collection) { - for (Object value : (Collection) param.getValue()) { - multipart.addTextBody(param.getKey() + "[]", ObjectUtils.asString(value), contentType); - } - } else { - String value = param.getValue().toString(); - if (StringUtils.isNotBlank(value)) { - multipart.addTextBody(param.getKey(), value, contentType); - } - } - } - - if (file instanceof String && !StringUtils.isRemoteUrl((String) file)) { - File _file = new File((String) file); - if (!_file.isFile() && !_file.canRead()) { - throw new IOException("File not found or unreadable: " + file); - } - file = _file; - } - String filename = (String) options.get("filename"); - if (file instanceof File) { - if (filename == null) filename = ((File) file).getName(); - multipart.addBinaryBody("file", (File) file, ContentType.APPLICATION_OCTET_STREAM, filename); - } else if (file instanceof String) { - multipart.addTextBody("file", (String) file, contentType); - } else if (file instanceof byte[]) { - if (filename == null) filename = "file"; - multipart.addBinaryBody("file", (byte[]) file, ContentType.APPLICATION_OCTET_STREAM, filename); - } else if (file == null) { - // no-problem - } else { - throw new IOException("Unrecognized file parameter " + file); - } - postMethod.setEntity(multipart.build()); - - String responseData = null; - int code = 0; - CloseableHttpResponse response = client.execute(postMethod); - try { - code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - responseData = StringUtils.read(responseStream); - } finally { - response.close(); - } - - Map result = processResponse(returnError, code, responseData); - return result; - } -} diff --git a/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java b/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java deleted file mode 100644 index 0036d453..00000000 --- a/cloudinary-http44/src/main/java/com/cloudinary/http44/api/Response.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.cloudinary.http44.api; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.http.Header; -import org.apache.http.HttpResponse; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.RateLimit; -import com.cloudinary.utils.StringUtils; - -@SuppressWarnings("rawtypes") -public class Response extends HashMap implements ApiResponse { - private static final long serialVersionUID = -5458609797599845837L; - private HttpResponse response = null; - - @SuppressWarnings("unchecked") - public Response(HttpResponse response, Map result) { - super(result); - this.response = response; - } - - public HttpResponse getRawHttpResponse() { - return this.response; - } - - private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-FEATURE(\\w*)RATELIMIT(-LIMIT|-RESET|-REMAINING)", Pattern.CASE_INSENSITIVE); - private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat(RFC1123_PATTERN, Locale.ENGLISH); - - public Map rateLimits() throws java.text.ParseException { - Header[] headers = this.response.getAllHeaders(); - Map limits = new HashMap(); - for (Header header : headers) { - Matcher m = RATE_LIMIT_REGEX.matcher(header.getName()); - if (m.matches()) { - String limitName = "Api"; - RateLimit limit = null; - if (!StringUtils.isEmpty(m.group(1))) { - limitName = m.group(1); - } - limit = limits.get(limitName); - if (limit == null) { - limit = new RateLimit(); - } - if (m.group(2).equalsIgnoreCase("-limit")) { - limit.setLimit(Long.parseLong(header.getValue())); - } else if (m.group(2).equalsIgnoreCase("-remaining")) { - limit.setRemaining(Long.parseLong(header.getValue())); - } else if (m.group(2).equalsIgnoreCase("-reset")) { - limit.setReset(RFC1123.parse(header.getValue())); - } - limits.put(limitName, limit); - } - } - return limits; - } - - public RateLimit apiRateLimit() throws java.text.ParseException { - return rateLimits().get("Api"); - } -} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/AccountApiTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/AccountApiTest.java deleted file mode 100644 index 573a12e5..00000000 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/AccountApiTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class AccountApiTest extends AbstractAccountApiTest { -} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java deleted file mode 100644 index c39a89e2..00000000 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/ApiTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.cloudinary.test; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.utils.ObjectUtils; -import org.apache.http.conn.ConnectTimeoutException; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import java.net.SocketTimeoutException; -import java.util.Map; - -public class ApiTest extends AbstractApiTest { - @Category(TimeoutTest.class) - @Test(expected = ConnectTimeoutException.class) - public void testConnectTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "connect_timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } - - @Category(TimeoutTest.class) - @Test(expected = SocketTimeoutException.class) - public void testTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } -} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/ContextTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/ContextTest.java deleted file mode 100644 index 4841e9f6..00000000 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/ContextTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.cloudinary.test; - -public class ContextTest extends AbstractContextTest { - -} \ No newline at end of file diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/FoldersApiTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/FoldersApiTest.java deleted file mode 100644 index 971bcf39..00000000 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/FoldersApiTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class FoldersApiTest extends AbstractFoldersApiTest { -} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/SearchTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/SearchTest.java deleted file mode 100644 index 16a4708c..00000000 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/SearchTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class SearchTest extends AbstractSearchTest { -} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java deleted file mode 100644 index 4e763579..00000000 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.cloudinary.test; - -/** - * Created by amir on 25/10/2016. - */ -public class StreamingProfilesApiTest extends AbstractStreamingProfilesApiTest { -} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/StructuredMetadataTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/StructuredMetadataTest.java deleted file mode 100644 index 8cc186f4..00000000 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/StructuredMetadataTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class StructuredMetadataTest extends AbstractStructuredMetadataTest { -} diff --git a/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java deleted file mode 100644 index 4734707c..00000000 --- a/cloudinary-http44/src/test/java/com/cloudinary/test/UploaderTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.cloudinary.test; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.utils.ObjectUtils; -import org.apache.http.conn.ConnectTimeoutException; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import java.net.SocketTimeoutException; -import java.util.Map; - -public class UploaderTest extends AbstractUploaderTest { - - @Category(TimeoutTest.class) - @Test(expected = ConnectTimeoutException.class) - public void testConnectTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "connect_timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } - - @Category(TimeoutTest.class) - @Test(expected = SocketTimeoutException.class) - public void testTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } -} diff --git a/cloudinary-http45/build.gradle b/cloudinary-http45/build.gradle deleted file mode 100644 index f4b7613d..00000000 --- a/cloudinary-http45/build.gradle +++ /dev/null @@ -1,113 +0,0 @@ -plugins { - id 'java-library' - id 'signing' - id 'maven-publish' - id 'io.codearte.nexus-staging' version '0.21.1' -} - -apply from: "../java_shared.gradle" - -task ciTest( type: Test ) { - useJUnit { - excludeCategories 'com.cloudinary.test.TimeoutTest' - if (System.getProperty("CLOUDINARY_ACCOUNT_URL") == "") { - exclude '**/AccountApiTest.class' - } - } -} - -dependencies { - compile project(':cloudinary-core') - compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' - compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.13' - compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.5.13' - testCompile project(':cloudinary-test-common') - testCompile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' - testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' - testCompile group: 'junit', name: 'junit', version: '4.12' -} - -if (hasProperty("ossrhPassword")) { - - signing { - sign configurations.archives - } - - nexusStaging { - packageGroup = group - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - pom { - name = 'Cloudinary Apache HTTP 4.5 Library' - packaging = 'jar' - groupId = publishGroupId - artifactId = 'cloudinary-http45' - description = publishDescription - url = githubUrl - licenses { - license { - name = licenseName - url = licenseUrl - } - } - - developers { - developer { - id = developerId - name = developerName - email = developerEmail - } - } - scm { - connection = scmConnection - developerConnection = scmDeveloperConnection - url = scmUrl - } - } - - pom.withXml { - def pomFile = file("${project.buildDir}/generated-pom.xml") - writeTo(pomFile) - def pomAscFile = signing.sign(pomFile).signatureFiles[0] - artifact(pomAscFile) { - classifier = null - extension = 'pom.asc' - } - } - - // create the signed artifacts - project.tasks.signArchives.signatureFiles.each { - artifact(it) { - def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ - if (matcher.find()) { - classifier = matcher.group(1) - } else { - classifier = null - } - extension = 'jar.asc' - } - } - } - } - - model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("$buildDir/generated-pom.xml") - } - tasks.publishMavenJavaPublicationToMavenLocal { - dependsOn project.tasks.signArchives - } - tasks.publishMavenJavaPublicationToSonatypeRepository { - dependsOn project.tasks.signArchives - } - } - } -} diff --git a/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java b/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java deleted file mode 100644 index b4e8673a..00000000 --- a/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiStrategy.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.cloudinary.http45; - -import com.cloudinary.Api; -import com.cloudinary.Api.HttpMethod; -import com.cloudinary.Cloudinary; -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.exceptions.GeneralError; -import com.cloudinary.http45.api.Response; -import com.cloudinary.utils.Base64Coder; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; -import org.apache.http.Consts; -import org.apache.http.HttpHost; -import org.apache.http.NameValuePair; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.conn.HttpClientConnectionManager; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.cloudinary.json.JSONException; -import org.cloudinary.json.JSONObject; - -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Constructor; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static com.cloudinary.http45.ApiUtils.prepareParams; -import static com.cloudinary.http45.ApiUtils.setTimeouts; - -public class ApiStrategy extends com.cloudinary.strategies.AbstractApiStrategy { - - private CloseableHttpClient client = null; - - @Override - public void init(Api api) { - super.init(api); - - HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(this.api.cloudinary.getUserAgent() + " ApacheHTTPComponents/4.5"); - - // If the configuration specifies a proxy then apply it to the client - if (api.cloudinary.config.proxyHost != null && api.cloudinary.config.proxyPort != 0) { - HttpHost proxy = new HttpHost(api.cloudinary.config.proxyHost, api.cloudinary.config.proxyPort); - clientBuilder.setProxy(proxy); - } - - HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) api.cloudinary.config.properties.get("connectionManager"); - if (connectionManager != null) { - clientBuilder.setConnectionManager(connectionManager); - } - - int timeout = this.api.cloudinary.config.timeout; - if (timeout > 0) { - RequestConfig config = RequestConfig.custom() - .setSocketTimeout(timeout * 1000) - .setConnectTimeout(timeout * 1000) - .build(); - clientBuilder.setDefaultRequestConfig(config); - } - - this.client = clientBuilder.build(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - public ApiResponse callApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - - String apiKey = ObjectUtils.asString(options.get("api_key"), this.api.cloudinary.config.apiKey); - String apiSecret = ObjectUtils.asString(options.get("api_secret"), this.api.cloudinary.config.apiSecret); - String oauthToken = ObjectUtils.asString(options.get("oauth_token"), this.api.cloudinary.config.oauthToken); - - validateAuthorization(apiKey, apiSecret, oauthToken); - - String apiUrl = createApiUrl(uri, options); - HttpUriRequest request = prepareRequest(method, apiUrl, params, options); - - request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, oauthToken)); - - return getApiResponse(request); - } - - private ApiResponse getApiResponse(HttpUriRequest request) throws Exception { - String responseData = null; - int code = 0; - CloseableHttpResponse response = client.execute(request); - try { - code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - responseData = StringUtils.read(responseStream); - } finally { - response.close(); - } - - Class exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code); - if (code != 200 && exceptionClass == null) { - throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData); - } - Map result; - - try { - JSONObject responseJSON = new JSONObject(responseData); - result = ObjectUtils.toMap(responseJSON); - } catch (JSONException e) { - throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); - } - - if (code == 200) { - return new Response(response, result); - } else { - String message = (String) ((Map) result.get("error")).get("message"); - Constructor exceptionConstructor = exceptionClass.getConstructor(String.class); - throw exceptionConstructor.newInstance(message); - } - } - - @Override - public ApiResponse callAccountApi(HttpMethod method, Iterable uri, Map params, Map options) throws Exception { - if (options == null) - options = ObjectUtils.emptyMap(); - - String prefix = ObjectUtils.asString(options.get("upload_prefix"), "https://api.cloudinary.com"); - String apiKey = ObjectUtils.asString(options.get("provisioning_api_key")); - if (apiKey == null) throw new IllegalArgumentException("Must supply provisioning_api_key"); - String apiSecret = ObjectUtils.asString(options.get("provisioning_api_secret")); - if (apiSecret == null) throw new IllegalArgumentException("Must supply provisioning_api_secret"); - - String apiUrl = StringUtils.join(Arrays.asList(prefix, "v1_1"), "/"); - for (String component : uri) { - apiUrl = apiUrl + "/" + component; - } - - HttpUriRequest request = prepareRequest(method, apiUrl, params, options); - - request.setHeader("Authorization", getAuthorizationHeaderValue(apiKey, apiSecret, null)); - - return getApiResponse(request); - } - - /** - * Prepare a request with the URL and parameters based on the HTTP method used - * - * @param method the HTTP method: GET, PUT, POST, DELETE - * @param apiUrl the cloudinary API URI - * @param params the parameters to pass to the server - * @return an HTTP request - * @throws URISyntaxException - * @throws UnsupportedEncodingException - */ - private HttpUriRequest prepareRequest(HttpMethod method, String apiUrl, Map params, Map options) throws URISyntaxException, UnsupportedEncodingException { - URI apiUri; - HttpRequestBase request; - - String contentType = ObjectUtils.asString(options.get("content_type"), "urlencoded"); - URIBuilder apiUrlBuilder = new URIBuilder(apiUrl); - List urlEncodedParams = prepareParams(params); - - if (method == HttpMethod.GET) { - apiUrlBuilder.setParameters(prepareParams(params)); - apiUri = apiUrlBuilder.build(); - request = new HttpGet(apiUri); - } else { - Map paramsCopy = new HashMap((Map) params); - apiUri = apiUrlBuilder.build(); - switch (method) { - case PUT: - request = new HttpPut(apiUri); - break; - case DELETE: //uses HttpPost instead of HttpDelete - paramsCopy.put("_method", "delete"); - //continue with POST - case POST: - request = new HttpPost(apiUri); - break; - default: - throw new IllegalArgumentException("Unknown HTTP method"); - } - if (contentType.equals("json")) { - JSONObject asJSON = ObjectUtils.toJSON(paramsCopy); - StringEntity requestEntity = new StringEntity(asJSON.toString(), ContentType.APPLICATION_JSON); - ((HttpEntityEnclosingRequestBase) request).setEntity(requestEntity); - } else { - ((HttpEntityEnclosingRequestBase) request).setEntity(new UrlEncodedFormEntity(prepareParams(paramsCopy), Consts.UTF_8)); - } - } - - setTimeouts(request, options); - return request; - } -} diff --git a/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiUtils.java b/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiUtils.java deleted file mode 100644 index 6639b9cc..00000000 --- a/cloudinary-http45/src/main/java/com/cloudinary/http45/ApiUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.cloudinary.http45; - -import com.cloudinary.utils.ObjectUtils; -import org.apache.http.NameValuePair; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.message.BasicNameValuePair; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class ApiUtils { - - public static void setTimeouts(HttpRequestBase request, Map options) { - RequestConfig config= request.getConfig(); - final RequestConfig.Builder builder; - if (config != null) { - builder = RequestConfig.copy(config); - } else { - builder = RequestConfig.custom(); - } - Integer timeout = (Integer) options.get("timeout"); - if(timeout != null) { - builder.setSocketTimeout(timeout); - } - Integer connectionRequestTimeout = (Integer) options.get("connection_request_timeout"); - if(connectionRequestTimeout != null) { - builder.setConnectionRequestTimeout(connectionRequestTimeout); - } - Integer connectTimeout = (Integer) options.get("connect_timeout"); - if(connectTimeout != null) { - builder.setConnectTimeout(connectTimeout); - } - request.setConfig(builder.build()); - } - - static List prepareParams(Map params) { - List requestParams = new ArrayList(params.size()); - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Iterable) { - for (Object single : (Iterable) param.getValue()) { - requestParams.add(new BasicNameValuePair(param.getKey() + "[]", ObjectUtils.asString(single))); - } - } else { - requestParams.add(new BasicNameValuePair(param.getKey(), ObjectUtils.asString(param.getValue()))); - } - } - - - return requestParams; - } -} diff --git a/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java b/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java deleted file mode 100644 index f4712515..00000000 --- a/cloudinary-http45/src/main/java/com/cloudinary/http45/UploaderStrategy.java +++ /dev/null @@ -1,141 +0,0 @@ -package com.cloudinary.http45; - -import com.cloudinary.Cloudinary; -import com.cloudinary.ProgressCallback; -import com.cloudinary.Uploader; -import com.cloudinary.Util; -import com.cloudinary.strategies.AbstractUploaderStrategy; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; -import org.apache.http.HttpHost; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.conn.HttpClientConnectionManager; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MIME; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Map; - -public class UploaderStrategy extends AbstractUploaderStrategy { - - private CloseableHttpClient client = null; - - @Override - public void init(Uploader uploader) { - super.init(uploader); - - HttpClientBuilder clientBuilder = HttpClients.custom(); - clientBuilder.useSystemProperties().setUserAgent(cloudinary().getUserAgent() + " ApacheHTTPComponents/4.5"); - - // If the configuration specifies a proxy then apply it to the client - if (cloudinary().config.proxyHost != null && cloudinary().config.proxyPort != 0) { - HttpHost proxy = new HttpHost(cloudinary().config.proxyHost, cloudinary().config.proxyPort); - clientBuilder.setProxy(proxy); - } - - HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) cloudinary().config.properties.get("connectionManager"); - if (connectionManager != null) { - clientBuilder.setConnectionManager(connectionManager); - } - - this.client = clientBuilder.build(); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Override - public Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException { - if (progressCallback != null){ - throw new IllegalArgumentException("Progress callback is not supported"); - } - - // initialize options if passed as null - if (options == null) { - options = ObjectUtils.emptyMap(); - } - - boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); - - if (requiresSigning(action, options)) { - uploader.signRequestParams(params, options); - } else { - Util.clearEmpty(params); - } - - String apiUrl = buildUploadUrl(action, options); - - HttpPost postMethod = new HttpPost(apiUrl); - ApiUtils.setTimeouts(postMethod, options); - - Map extraHeaders = (Map) options.get("extra_headers"); - if (extraHeaders != null) { - for (Map.Entry header : extraHeaders.entrySet()) { - postMethod.setHeader(header.getKey(), header.getValue()); - } - } - - MultipartEntityBuilder multipart = MultipartEntityBuilder.create(); - multipart.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); - multipart.setCharset(StandardCharsets.UTF_8); - ContentType contentType = ContentType.MULTIPART_FORM_DATA.withCharset(MIME.UTF8_CHARSET); - // Remove blank parameters - for (Map.Entry param : params.entrySet()) { - if (param.getValue() instanceof Collection) { - for (Object value : (Collection) param.getValue()) { - multipart.addTextBody(param.getKey() + "[]", ObjectUtils.asString(value), contentType); - } - } else { - String value = param.getValue().toString(); - if (StringUtils.isNotBlank(value)) { - multipart.addTextBody(param.getKey(), value, contentType); - } - } - } - - if (file instanceof String && !StringUtils.isRemoteUrl((String) file)) { - File _file = new File((String) file); - if (!_file.isFile() && !_file.canRead()) { - throw new IOException("File not found or unreadable: " + file); - } - file = _file; - } - String filename = (String) options.get("filename"); - if (file instanceof File) { - if (filename == null) filename = ((File) file).getName(); - multipart.addBinaryBody("file", (File) file, ContentType.APPLICATION_OCTET_STREAM, filename); - } else if (file instanceof String) { - multipart.addTextBody("file", (String) file, contentType); - } else if (file instanceof byte[]) { - if (filename == null) filename = "file"; - multipart.addBinaryBody("file", (byte[]) file, ContentType.APPLICATION_OCTET_STREAM, filename); - } else if (file == null) { - // no-problem - } else { - throw new IOException("Unrecognized file parameter " + file); - } - postMethod.setEntity(multipart.build()); - - String responseData = null; - int code = 0; - CloseableHttpResponse response = client.execute(postMethod); - try { - code = response.getStatusLine().getStatusCode(); - InputStream responseStream = response.getEntity().getContent(); - responseData = StringUtils.read(responseStream); - } finally { - response.close(); - } - - Map result = processResponse(returnError, code, responseData); - return result; - } -} diff --git a/cloudinary-http45/src/main/java/com/cloudinary/http45/api/Response.java b/cloudinary-http45/src/main/java/com/cloudinary/http45/api/Response.java deleted file mode 100644 index 08121401..00000000 --- a/cloudinary-http45/src/main/java/com/cloudinary/http45/api/Response.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.cloudinary.http45.api; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.http.Header; -import org.apache.http.HttpResponse; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.RateLimit; -import com.cloudinary.utils.StringUtils; - -@SuppressWarnings("rawtypes") -public class Response extends HashMap implements ApiResponse { - private static final long serialVersionUID = -5458609797599845837L; - private HttpResponse response = null; - - @SuppressWarnings("unchecked") - public Response(HttpResponse response, Map result) { - super(result); - this.response = response; - } - - public HttpResponse getRawHttpResponse() { - return this.response; - } - - private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-FEATURE(\\w*)RATELIMIT(-LIMIT|-RESET|-REMAINING)", Pattern.CASE_INSENSITIVE); - private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; - private static final DateFormat RFC1123 = new SimpleDateFormat(RFC1123_PATTERN, Locale.ENGLISH); - - public Map rateLimits() throws java.text.ParseException { - Header[] headers = this.response.getAllHeaders(); - Map limits = new HashMap(); - for (Header header : headers) { - Matcher m = RATE_LIMIT_REGEX.matcher(header.getName()); - if (m.matches()) { - String limitName = "Api"; - RateLimit limit = null; - if (!StringUtils.isEmpty(m.group(1))) { - limitName = m.group(1); - } - limit = limits.get(limitName); - if (limit == null) { - limit = new RateLimit(); - } - if (m.group(2).equalsIgnoreCase("-limit")) { - limit.setLimit(Long.parseLong(header.getValue())); - } else if (m.group(2).equalsIgnoreCase("-remaining")) { - limit.setRemaining(Long.parseLong(header.getValue())); - } else if (m.group(2).equalsIgnoreCase("-reset")) { - limit.setReset(RFC1123.parse(header.getValue())); - } - limits.put(limitName, limit); - } - } - return limits; - } - - public RateLimit apiRateLimit() throws java.text.ParseException { - return rateLimits().get("Api"); - } -} diff --git a/cloudinary-http45/src/test/java/com/cloudinary/test/AccountApiTest.java b/cloudinary-http45/src/test/java/com/cloudinary/test/AccountApiTest.java deleted file mode 100644 index 573a12e5..00000000 --- a/cloudinary-http45/src/test/java/com/cloudinary/test/AccountApiTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class AccountApiTest extends AbstractAccountApiTest { -} diff --git a/cloudinary-http45/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http45/src/test/java/com/cloudinary/test/ApiTest.java deleted file mode 100644 index c39a89e2..00000000 --- a/cloudinary-http45/src/test/java/com/cloudinary/test/ApiTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.cloudinary.test; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.utils.ObjectUtils; -import org.apache.http.conn.ConnectTimeoutException; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import java.net.SocketTimeoutException; -import java.util.Map; - -public class ApiTest extends AbstractApiTest { - @Category(TimeoutTest.class) - @Test(expected = ConnectTimeoutException.class) - public void testConnectTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "connect_timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } - - @Category(TimeoutTest.class) - @Test(expected = SocketTimeoutException.class) - public void testTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } -} diff --git a/cloudinary-http45/src/test/java/com/cloudinary/test/ContextTest.java b/cloudinary-http45/src/test/java/com/cloudinary/test/ContextTest.java deleted file mode 100644 index 4841e9f6..00000000 --- a/cloudinary-http45/src/test/java/com/cloudinary/test/ContextTest.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.cloudinary.test; - -public class ContextTest extends AbstractContextTest { - -} \ No newline at end of file diff --git a/cloudinary-http45/src/test/java/com/cloudinary/test/FoldersApiTest.java b/cloudinary-http45/src/test/java/com/cloudinary/test/FoldersApiTest.java deleted file mode 100644 index 971bcf39..00000000 --- a/cloudinary-http45/src/test/java/com/cloudinary/test/FoldersApiTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class FoldersApiTest extends AbstractFoldersApiTest { -} diff --git a/cloudinary-http45/src/test/java/com/cloudinary/test/SearchTest.java b/cloudinary-http45/src/test/java/com/cloudinary/test/SearchTest.java deleted file mode 100644 index 16a4708c..00000000 --- a/cloudinary-http45/src/test/java/com/cloudinary/test/SearchTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class SearchTest extends AbstractSearchTest { -} diff --git a/cloudinary-http45/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java b/cloudinary-http45/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java deleted file mode 100644 index 6a2e8a31..00000000 --- a/cloudinary-http45/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class StreamingProfilesApiTest extends AbstractStreamingProfilesApiTest { -} diff --git a/cloudinary-http45/src/test/java/com/cloudinary/test/StructuredMetadataTest.java b/cloudinary-http45/src/test/java/com/cloudinary/test/StructuredMetadataTest.java deleted file mode 100644 index 8cc186f4..00000000 --- a/cloudinary-http45/src/test/java/com/cloudinary/test/StructuredMetadataTest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.cloudinary.test; - -public class StructuredMetadataTest extends AbstractStructuredMetadataTest { -} diff --git a/cloudinary-http45/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http45/src/test/java/com/cloudinary/test/UploaderTest.java deleted file mode 100644 index efbf9190..00000000 --- a/cloudinary-http45/src/test/java/com/cloudinary/test/UploaderTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.cloudinary.test; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.utils.ObjectUtils; -import org.apache.http.conn.ConnectTimeoutException; -import org.junit.Test; -import org.junit.experimental.categories.Category; - -import java.net.SocketTimeoutException; -import java.util.Map; - -public class UploaderTest extends AbstractUploaderTest { - - @Category(TimeoutTest.class) - @Test(expected = ConnectTimeoutException.class) - public void testConnectTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "connect_timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } - - @Category(TimeoutTest.class) - @Test(expected = SocketTimeoutException.class) - public void testTimeoutParameter() throws Exception { - // should allow listing resources - Map options = ObjectUtils.asMap( - "max_results", 500, - "timeout", 1); - ApiResponse result = cloudinary.api().resources(options); - } - -} \ No newline at end of file diff --git a/cloudinary-http43/build.gradle b/cloudinary-http5/build.gradle similarity index 92% rename from cloudinary-http43/build.gradle rename to cloudinary-http5/build.gradle index 47fe4701..177ecdfa 100644 --- a/cloudinary-http43/build.gradle +++ b/cloudinary-http5/build.gradle @@ -19,8 +19,8 @@ task ciTest( type: Test ) { dependencies { compile project(':cloudinary-core') compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' - compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.3.1' - compile group: 'org.apache.httpcomponents', name: 'httpmime', version: '4.3' + api group: 'org.apache.httpcomponents.client5', name: 'httpclient5', version: '5.3.1' + api group: 'org.apache.httpcomponents.core5', name: 'httpcore5', version: '5.2.5' testCompile project(':cloudinary-test-common') testCompile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' @@ -46,10 +46,10 @@ if (hasProperty("ossrhPassword")) { artifact sourcesJar artifact javadocJar pom { - name = 'Cloudinary Apache HTTP 4.3 Library' + name = 'Cloudinary Apache HTTP 5 Library' packaging = 'jar' groupId = publishGroupId - artifactId = 'cloudinary-http43' + artifactId = 'cloudinary-http5' description = publishDescription url = githubUrl licenses { diff --git a/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java new file mode 100644 index 00000000..2852225d --- /dev/null +++ b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java @@ -0,0 +1,163 @@ +package com.cloudinary.http5; + + +import com.cloudinary.Api; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.exceptions.GeneralError; +import com.cloudinary.http5.api.Response; +import com.cloudinary.strategies.AbstractApiStrategy; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; +import org.apache.hc.client5.http.classic.methods.*; +import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.net.URIBuilder; +import org.cloudinary.json.JSONException; +import org.cloudinary.json.JSONObject; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static com.cloudinary.http5.ApiUtils.prepareParams; +import static com.cloudinary.http5.ApiUtils.setTimeouts; + +public class ApiStrategy extends AbstractApiStrategy { + + private static final String APACHE_HTTP_CLIENT_VERSION = System.getProperty("apache.http.client.version", "5.3.1"); + + private CloseableHttpClient client; + + @Override + public void init(Api api) { + super.init(api); + + this.client = HttpClients.custom() + .setUserAgent(this.api.cloudinary.getUserAgent() + " ApacheHttpClient/" + APACHE_HTTP_CLIENT_VERSION) + .build(); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public ApiResponse callApi(Api.HttpMethod method, String apiUrl, Map params, Map options, String autorizationHeader) throws Exception { + HttpUriRequestBase request = prepareRequest(method, apiUrl, params, options); + + request.setHeader("Authorization", autorizationHeader); + + return getApiResponse(request); + } + + private ApiResponse getApiResponse(HttpUriRequestBase request) throws Exception { + String responseData = null; + int code = 0; + CloseableHttpResponse response; + try { + response = client.execute(request); + code = response.getCode(); + HttpEntity entity = response.getEntity(); + if (entity != null) { + responseData = EntityUtils.toString(entity, StandardCharsets.UTF_8); + } + } catch (IOException e) { + throw new GeneralError("Error executing request: " + e.getMessage()); + } + + if (code != 200) { + Map result; + try { + JSONObject responseJSON = new JSONObject(responseData); + result = ObjectUtils.toMap(responseJSON); + } catch (JSONException e) { + throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); + } + + // Extract the error message from the result map + String message = (String) ((Map) result.get("error")).get("message"); + + // Get the appropriate exception class based on status code + Class exceptionClass = Api.CLOUDINARY_API_ERROR_CLASSES.get(code); + if (exceptionClass != null) { + Constructor exceptionConstructor = exceptionClass.getConstructor(String.class); + throw exceptionConstructor.newInstance(message); + } else { + throw new GeneralError("Server returned unexpected status code - " + code + " - " + responseData); + } + } + + Map result; + try { + JSONObject responseJSON = new JSONObject(responseData); + result = ObjectUtils.toMap(responseJSON); + } catch (JSONException e) { + throw new RuntimeException("Invalid JSON response from server " + e.getMessage()); + } + + return new Response(response, result); + } + + @Override + public ApiResponse callAccountApi(Api.HttpMethod method, String apiUrl, Map params, Map options, String authorizationHeader) throws Exception { + // Prepare the request + HttpUriRequestBase request = prepareRequest(method, apiUrl, params, options); + + // Add authorization header + + request.setHeader("Authorization", authorizationHeader); + + // Execute the request and return the response + return getApiResponse(request); + } + + private HttpUriRequestBase prepareRequest(Api.HttpMethod method, String apiUrl, Map params, Map options) throws URISyntaxException { + HttpUriRequestBase request; + + String contentType = ObjectUtils.asString(options.get("content_type"), "urlencoded"); + + switch (method) { + case GET: + URIBuilder uriBuilder = new URIBuilder(apiUrl); + for (NameValuePair param : prepareParams(params)) { + uriBuilder.addParameter(param.getName(), param.getValue()); + } + request = new HttpGet(uriBuilder.toString()); + break; + case POST: + request = new HttpPost(apiUrl); + setEntity((HttpUriRequestBase) request, params, contentType); + break; + case PUT: + request = new HttpPut(apiUrl); + setEntity((HttpUriRequestBase) request, params, contentType); + break; + case DELETE: + request = new HttpDelete(apiUrl); + setEntity((HttpUriRequestBase) request, params, contentType); + break; + default: + throw new IllegalArgumentException("Unknown HTTP method"); + } + setTimeouts(request, options); + return request; + } + + private void setEntity(HttpUriRequestBase request, Map params, String contentType) { + if ("json".equals(contentType)) { + JSONObject json = ObjectUtils.toJSON(params); + StringEntity entity = new StringEntity(json.toString(), StandardCharsets.UTF_8); + request.setEntity(entity); + request.setHeader("Content-Type", "application/json"); + } else { + List formParams = prepareParams(params); + request.setEntity(new UrlEncodedFormEntity(formParams, StandardCharsets.UTF_8)); + } + } +} diff --git a/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiUtils.java b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiUtils.java new file mode 100644 index 00000000..af67a1e8 --- /dev/null +++ b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiUtils.java @@ -0,0 +1,71 @@ +package com.cloudinary.http5; + +import com.cloudinary.utils.ObjectUtils; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.message.BasicNameValuePair; +import org.apache.hc.core5.util.Timeout; +import org.cloudinary.json.JSONObject; + +import java.util.*; + +public class ApiUtils { + + public static void setTimeouts(HttpUriRequestBase request, Map options) { + RequestConfig config = request.getConfig(); + final RequestConfig.Builder builder; + + if (config != null) { + builder = RequestConfig.copy(config); + } else { + builder = RequestConfig.custom(); + } + + Integer timeout = (Integer) options.get("timeout"); + if (timeout != null) { + builder.setResponseTimeout(Timeout.ofSeconds(timeout)); + } + + Integer connectionRequestTimeout = (Integer) options.get("connection_request_timeout"); + if (connectionRequestTimeout != null) { + builder.setConnectionRequestTimeout(Timeout.ofSeconds(connectionRequestTimeout)); + } + + Integer connectTimeout = (Integer) options.get("connect_timeout"); + if (connectTimeout != null) { + builder.setConnectTimeout(Timeout.ofSeconds(connectTimeout)); + } + + request.setConfig(builder.build()); + } + + + public static List prepareParams(Map params) { + List requestParams = new ArrayList<>(); + + for (Map.Entry param : params.entrySet()) { + String key = param.getKey(); + Object value = param.getValue(); + + if (value instanceof Iterable) { + // If the value is an Iterable, handle each item individually + for (Object single : (Iterable) value) { + requestParams.add(new BasicNameValuePair(key + "[]", ObjectUtils.asString(single))); + } + } else if (value instanceof Map) { + // Convert Map to JSON string manually to avoid empty object issues + JSONObject jsonObject = new JSONObject(); + for (Map.Entry entry : ((Map) value).entrySet()) { + jsonObject.put(entry.getKey().toString(), entry.getValue()); + } + requestParams.add(new BasicNameValuePair(key, jsonObject.toString())); + } else { + // Handle simple key-value pairs + requestParams.add(new BasicNameValuePair(key, ObjectUtils.asString(value))); + } + } + + return requestParams; + } +} diff --git a/cloudinary-http5/src/main/java/com/cloudinary/http5/UploaderStrategy.java b/cloudinary-http5/src/main/java/com/cloudinary/http5/UploaderStrategy.java new file mode 100644 index 00000000..5f87cbd8 --- /dev/null +++ b/cloudinary-http5/src/main/java/com/cloudinary/http5/UploaderStrategy.java @@ -0,0 +1,155 @@ +package com.cloudinary.http5; + +import com.cloudinary.ProgressCallback; +import com.cloudinary.Uploader; +import com.cloudinary.Util; +import com.cloudinary.strategies.AbstractUploaderStrategy; +import com.cloudinary.utils.ObjectUtils; +import com.cloudinary.utils.StringUtils; +import org.apache.hc.client5.http.classic.methods.HttpPost; +import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.entity.mime.ByteArrayBody; +import org.apache.hc.client5.http.entity.mime.FileBody; +import org.apache.hc.client5.http.entity.mime.HttpMultipartMode; +import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Map; + +public class UploaderStrategy extends AbstractUploaderStrategy { + + private static final String APACHE_HTTP_CLIENT_VERSION = System.getProperty("apache.http.client.version", "5.3.1"); + + private CloseableHttpClient client; + + @Override + public void init(Uploader uploader) { + super.init(uploader); + + this.client = HttpClients.custom() + .setUserAgent(cloudinary().getUserAgent() + " ApacheHttpClient/" + APACHE_HTTP_CLIENT_VERSION) + .build(); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException { + if (progressCallback != null) { + throw new IllegalArgumentException("Progress callback is not supported"); + } + + // Initialize options if passed as null + if (options == null) { + options = ObjectUtils.emptyMap(); + } + + boolean returnError = ObjectUtils.asBoolean(options.get("return_error"), false); + + if (requiresSigning(action, options)) { + uploader.signRequestParams(params, options); + } else { + Util.clearEmpty(params); + } + + String apiUrl = buildUploadUrl(action, options); + + // Prepare the request + HttpUriRequestBase request = prepareRequest(apiUrl, params, options, file); + + // Execute the request and handle the response + String responseData; + int code; + + try (CloseableHttpResponse response = client.execute(request)) { + code = response.getCode(); + responseData = EntityUtils.toString(response.getEntity()); + } catch (ParseException e) { + throw new RuntimeException(e); + } + + // Process and return the response + return processResponse(returnError, code, responseData); + } + + private HttpUriRequestBase prepareRequest(String apiUrl, Map params, Map options, Object file) throws IOException { + HttpPost request = new HttpPost(apiUrl); + + MultipartEntityBuilder multipartBuilder = MultipartEntityBuilder.create() + .setCharset(StandardCharsets.UTF_8).setMode(HttpMultipartMode.LEGACY); + + // Add text parameters + for (Map.Entry param : params.entrySet()) { + if (param.getValue() instanceof Collection) { + for (Object value : (Collection) param.getValue()) { + multipartBuilder.addTextBody(param.getKey() + "[]", ObjectUtils.asString(value), ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8)); + } + } else { + String value = param.getValue().toString(); + if (StringUtils.isNotBlank(value)) { + multipartBuilder.addTextBody(param.getKey(), value, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8)); + } + } + } + + // Add file part + addFilePart(multipartBuilder, file, options); + + request.setEntity(multipartBuilder.build()); + + // Add extra headers if provided + Map extraHeaders = (Map) options.get("extra_headers"); + if (extraHeaders != null) { + for (Map.Entry header : extraHeaders.entrySet()) { + request.addHeader(header.getKey(), header.getValue()); + } + } + + return request; + } + + + private void addFilePart(MultipartEntityBuilder multipartBuilder, Object file, Map options) throws IOException { + String filename = (String) options.get("filename"); + + if (file instanceof String && !StringUtils.isRemoteUrl((String) file)) { + File _file = new File((String) file); + if (!_file.isFile() || !_file.canRead()) { + throw new IOException("File not found or unreadable: " + file); + } + file = _file; + } + + if (file instanceof File) { + if (filename == null) { + filename = ((File) file).getName(); + } + // Encode filename properly + filename = new String(filename.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + + // Create FileBody with correct filename encoding + FileBody fileBody = new FileBody((File) file, ContentType.APPLICATION_OCTET_STREAM, filename); + multipartBuilder.addPart("file", fileBody); + } else if (file instanceof String) { + multipartBuilder.addTextBody("file", (String) file, ContentType.TEXT_PLAIN); + } else if (file instanceof byte[]) { + if (filename == null) { + filename = "file"; + } + ByteArrayBody byteArrayBody = new ByteArrayBody((byte[]) file, ContentType.APPLICATION_OCTET_STREAM, filename); + multipartBuilder.addPart("file", byteArrayBody); + } else if (file == null) { + // No file to add + } else { + throw new IOException("Unrecognized file parameter " + file); + } + } +} diff --git a/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java b/cloudinary-http5/src/main/java/com/cloudinary/http5/api/Response.java similarity index 72% rename from cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java rename to cloudinary-http5/src/main/java/com/cloudinary/http5/api/Response.java index 87de8299..fd7b0980 100644 --- a/cloudinary-http42/src/main/java/com/cloudinary/http42/api/Response.java +++ b/cloudinary-http5/src/main/java/com/cloudinary/http5/api/Response.java @@ -1,7 +1,12 @@ -package com.cloudinary.http42.api; +package com.cloudinary.http5.api; -import java.text.DateFormat; +import com.cloudinary.api.ApiResponse; +import com.cloudinary.api.RateLimit; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpResponse; import java.text.ParseException; + +import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.HashMap; import java.util.Locale; @@ -9,20 +14,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.http.Header; -import org.apache.http.HttpResponse; - -import com.cloudinary.api.ApiResponse; -import com.cloudinary.api.RateLimit; -import com.cloudinary.utils.StringUtils; - -@SuppressWarnings("rawtypes") public class Response extends HashMap implements ApiResponse { private static final long serialVersionUID = -5458609797599845837L; - private HttpResponse response = null; + private final HttpResponse response; @SuppressWarnings("unchecked") - public Response(HttpResponse response, Map result) { + public Response(HttpResponse response, Map result) { super(result); this.response = response; } @@ -32,25 +29,21 @@ public HttpResponse getRawHttpResponse() { } private static final Pattern RATE_LIMIT_REGEX = Pattern - .compile("X-FEATURE(\\w*)RATELIMIT(-LIMIT|-RESET|-REMAINING)", Pattern.CASE_INSENSITIVE); - private static final String RFC1123_PATTERN = "EEE, dd MMM yyyyy HH:mm:ss z"; + .compile("X-FEATURE(\\w*)RATELIMIT(-LIMIT|-RESET|-REMAINING)", Pattern.CASE_INSENSITIVE); + private static final String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z"; private static final DateFormat RFC1123 = new SimpleDateFormat(RFC1123_PATTERN, Locale.ENGLISH); public Map rateLimits() throws ParseException { - Header[] headers = this.response.getAllHeaders(); - Map limits = new HashMap(); + Header[] headers = this.response.getHeaders(); + Map limits = new HashMap<>(); for (Header header : headers) { Matcher m = RATE_LIMIT_REGEX.matcher(header.getName()); if (m.matches()) { String limitName = "Api"; - RateLimit limit = null; - if (!StringUtils.isEmpty(m.group(1))) { + RateLimit limit = limits.getOrDefault(limitName, new RateLimit()); + if (!m.group(1).isEmpty()) { limitName = m.group(1); } - limit = limits.get(limitName); - if (limit == null) { - limit = new RateLimit(); - } if (m.group(2).equalsIgnoreCase("-limit")) { limit.setLimit(Long.parseLong(header.getValue())); } else if (m.group(2).equalsIgnoreCase("-remaining")) { diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/AccountApiTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/AccountApiTest.java similarity index 100% rename from cloudinary-http42/src/test/java/com/cloudinary/test/AccountApiTest.java rename to cloudinary-http5/src/test/java/com/cloudinary/test/AccountApiTest.java diff --git a/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java new file mode 100644 index 00000000..a2f3c89f --- /dev/null +++ b/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java @@ -0,0 +1,45 @@ +package com.cloudinary.test; + +import com.cloudinary.api.ApiResponse; +import com.cloudinary.utils.ObjectUtils; +import org.apache.hc.core5.util.Timeout; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import java.util.Map; + + +public class ApiTest extends AbstractApiTest { + + @Category(TimeoutTest.class) + @Test(expected = Exception.class) + public void testConnectTimeoutParameter() throws Exception { + Map options = ObjectUtils.asMap( + "max_results", 500, + "connect_timeout", 0.2); + + try { + System.out.println("Setting connect timeout to 100 ms"); + ApiResponse result = cloudinary.api().resources(options); + System.out.println("Request completed without timeout"); + } catch (Exception e) { + throw new Exception("Connection timeout", e); + } + } + + @Category(TimeoutTest.class) + @Test(expected = Exception.class) + public void testTimeoutParameter() throws Exception { + // Set a very short request timeout to trigger a timeout exception + Map options = ObjectUtils.asMap( + "max_results", 500, + "timeout", Timeout.ofMilliseconds(1000)); // Set the timeout to 1 second + + try { + ApiResponse result = cloudinary.api().resources(options); + } catch (Exception e) { + // Convert IOException to SocketTimeoutException if appropriate + throw new Exception("Socket timeout"); + } + } +} + diff --git a/cloudinary-http43/src/test/java/com/cloudinary/test/ContextTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/ContextTest.java similarity index 100% rename from cloudinary-http43/src/test/java/com/cloudinary/test/ContextTest.java rename to cloudinary-http5/src/test/java/com/cloudinary/test/ContextTest.java diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/FoldersApiTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/FoldersApiTest.java similarity index 100% rename from cloudinary-http42/src/test/java/com/cloudinary/test/FoldersApiTest.java rename to cloudinary-http5/src/test/java/com/cloudinary/test/FoldersApiTest.java diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/SearchTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/SearchTest.java similarity index 100% rename from cloudinary-http42/src/test/java/com/cloudinary/test/SearchTest.java rename to cloudinary-http5/src/test/java/com/cloudinary/test/SearchTest.java diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java similarity index 100% rename from cloudinary-http42/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java rename to cloudinary-http5/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/StructuredMetadataTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/StructuredMetadataTest.java similarity index 100% rename from cloudinary-http42/src/test/java/com/cloudinary/test/StructuredMetadataTest.java rename to cloudinary-http5/src/test/java/com/cloudinary/test/StructuredMetadataTest.java diff --git a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/UploaderTest.java similarity index 97% rename from cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java rename to cloudinary-http5/src/test/java/com/cloudinary/test/UploaderTest.java index 712b0ffd..50c2a6ed 100644 --- a/cloudinary-http42/src/test/java/com/cloudinary/test/UploaderTest.java +++ b/cloudinary-http5/src/test/java/com/cloudinary/test/UploaderTest.java @@ -2,4 +2,4 @@ public class UploaderTest extends AbstractUploaderTest { -} +} \ No newline at end of file diff --git a/samples/photo_album_gae/pom.xml b/samples/photo_album_gae/pom.xml index d730e1f5..9be3ebf6 100644 --- a/samples/photo_album_gae/pom.xml +++ b/samples/photo_album_gae/pom.xml @@ -42,8 +42,8 @@ com.cloudinary - cloudinary-http42 - 1.4.1 + cloudinary-http5 + 2.0.0 org.springframework diff --git a/settings.gradle b/settings.gradle index 174cb41d..004842ea 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,6 @@ rootProject.name = 'cloudinary-parent' include ':cloudinary-core' include ':cloudinary-taglib' +include ':cloudinary-http5' include ':cloudinary-test-common' -include ':cloudinary-http42' -include ':cloudinary-http43' -include ':cloudinary-http44' -include ':cloudinary-http45' + From 6bb07c2ca8bad7163d08df3c3d257acccde6bd0f Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 12 Sep 2024 07:54:01 +0300 Subject: [PATCH 18/40] Add `auto_chaptering` and `auto_transcription` to upload API --- .../src/main/java/com/cloudinary/Util.java | 9 ++++++++- .../cloudinary/test/AbstractUploaderTest.java | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index 99fa608c..c5fcb1f0 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -11,7 +11,8 @@ public class Util { static final String[] BOOLEAN_UPLOAD_OPTIONS = new String[]{"backup", "exif", "faces", "colors", "image_metadata", "use_filename", "unique_filename", "eager_async", "invalidate", "discard_original_filename", "overwrite", "phash", "return_delete_token", "async", "quality_analysis", "cinemagraph_analysis", - "accessibility_analysis", "use_filename_as_display_name", "use_asset_folder_as_public_id_prefix", "unique_display_name", "media_metadata", "visual_search"}; + "accessibility_analysis", "use_filename_as_display_name", "use_asset_folder_as_public_id_prefix", "unique_display_name", "media_metadata", "visual_search", + "auto_chaptering", "auto_transcription"}; @SuppressWarnings({"rawtypes", "unchecked"}) public static final Map buildUploadParams(Map options) { @@ -184,6 +185,12 @@ public static final void processWriteParameters(Map options, Map if(options.get("visual_search") != null) { params.put("visual_search", options.get("visual_search")); } + if(options.get("auto_chaptering") != null) { + params.put("auto_chaptering", options.get("auto_chaptering")); + } + if(options.get("auto_transcription") != null) { + params.put("auto_transcription", options.get("auto_transcription")); + } } protected static String encodeAccessControl(Object accessControl) { diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java index 8581ca30..19098f6c 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java @@ -841,4 +841,20 @@ public void testNotificationUrl() { Map uploadParams = Util.buildUploadParams(options); Assert.assertEquals("https://www.test.com", uploadParams.get("notification_url")); } + + @Test + public void testAutoChaptering() throws Exception { + Map result = cloudinary.uploader().upload(SRC_TEST_VIDEO, asMap( + "resource_type", "video", "auto_chaptering", true)); + assert(result != null); + assertNotNull(result.get("playback_url")); + } + + @Test + public void testAutoTranscription() throws Exception { + Map result = cloudinary.uploader().upload(SRC_TEST_VIDEO, asMap( + "resource_type", "video", "auto_transcription", true)); + assert(result != null); + assertNotNull(result.get("playback_url")); + } } From 94f8977106b394bab5c2d61dc3cb0fc154df6b0f Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Sun, 29 Sep 2024 15:09:20 +0300 Subject: [PATCH 19/40] Version 2.0.0 --- CHANGELOG.md | 9 +++++++++ README.md | 11 +++++++---- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4b4cc2c..bc13fee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +2.0.0 / 2024-09-29 +================== + +* Bump minimum Java version to 8 +* Secure true by default +* Add `auto_chaptering` and `auto_transcription` to upload API +* New Http client +* Add support for update metadata field set default disabled + 1.39.0 / 2024-07-14 =================== diff --git a/README.md b/README.md index 42a635c6..4e44ba97 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,12 @@ For the complete documentation, see the [Java SDK Guide](https://cloudinary.com/ - [Upload assets to cloud](https://cloudinary.com/documentation/java_image_and_video_upload) ## Version Support -| SDK Version | Java 6+ | -|----------------|---------| -| 1.1.0 - 1.39.0 | V | +| SDK Version | Java 6+ | Java 8 | +|----------------|---------|--------| +| 1.1.0 - 1.39.0 | V | | +| 2.0.0 | | V | + + ## Installation The cloudinary_java library is available in [Maven Central](https://mvnrepository.com/artifact/com.cloudinary/cloudinary-core). To use it, add the following dependency to your pom.xml : @@ -36,7 +39,7 @@ The cloudinary_java library is available in [Maven Central](https://mvnrepositor com.cloudinary cloudinary-http45 - 1.39.0 + 2.0.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 27b347ac..9bdbe122 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -32,7 +32,7 @@ public class Cloudinary { public final static String AKAMAI_SHARED_CDN = "res.cloudinary.com"; public final static String SHARED_CDN = AKAMAI_SHARED_CDN; - public final static String VERSION = "1.39.0"; + public final static String VERSION = "2.0.0"; static String USER_AGENT_PREFIX = "CloudinaryJava"; public final static String USER_AGENT_JAVA_VERSION = "(Java " + System.getProperty("java.version") + ")"; diff --git a/gradle.properties b/gradle.properties index e588ea7d..c15a7578 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=1.39.0 +version=2.0.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 43d8e9ac0c7f073017d9c4b566c9566dea95f205 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Sun, 29 Sep 2024 15:39:43 +0300 Subject: [PATCH 20/40] Fix publish script --- build.gradle | 4 ++-- cloudinary-core/build.gradle | 4 ++-- cloudinary-http5/build.gradle | 4 ++-- cloudinary-taglib/build.gradle | 4 ++-- cloudinary-test-common/build.gradle | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index ebe374c8..5b9d6f04 100644 --- a/build.gradle +++ b/build.gradle @@ -20,8 +20,8 @@ nexusPublishing { } repositories { sonatype { - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" + password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" } } } diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle index 37246e23..01ac348b 100644 --- a/cloudinary-core/build.gradle +++ b/cloudinary-core/build.gradle @@ -23,8 +23,8 @@ if (hasProperty("ossrhPassword")) { nexusStaging { packageGroup = group - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" + password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" } publishing { diff --git a/cloudinary-http5/build.gradle b/cloudinary-http5/build.gradle index 177ecdfa..b58b6c36 100644 --- a/cloudinary-http5/build.gradle +++ b/cloudinary-http5/build.gradle @@ -35,8 +35,8 @@ if (hasProperty("ossrhPassword")) { nexusStaging { packageGroup = group - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" + password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" } publishing { diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle index 6db5af30..16b200f3 100644 --- a/cloudinary-taglib/build.gradle +++ b/cloudinary-taglib/build.gradle @@ -30,8 +30,8 @@ if (hasProperty("ossrhPassword")) { nexusStaging { packageGroup = group - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" + password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" } publishing { diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle index 31a8bae2..daa5ce83 100644 --- a/cloudinary-test-common/build.gradle +++ b/cloudinary-test-common/build.gradle @@ -24,8 +24,8 @@ if (hasProperty("ossrhPassword")) { nexusStaging { packageGroup = group - username = project.hasProperty("ossrhUsername") ? project.ext["ossrhUsername"] : "" - password = project.hasProperty("ossrhPassword") ? project.ext["ossrhPassword"] : "" + username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" + password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" } publishing { From ab8f466972f0763c9230b683a5986e539a129cab Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Sun, 20 Oct 2024 18:57:29 +0200 Subject: [PATCH 21/40] Make utility classes proper utilities --- .../src/main/java/com/cloudinary/SmartUrlEncoder.java | 4 +++- cloudinary-core/src/main/java/com/cloudinary/Util.java | 4 +++- .../src/main/java/com/cloudinary/utils/Base64Map.java | 4 +++- .../src/main/java/com/cloudinary/utils/HtmlEscape.java | 3 ++- .../src/main/java/com/cloudinary/utils/ObjectUtils.java | 4 +++- .../src/main/java/com/cloudinary/utils/StringUtils.java | 4 +++- .../src/main/java/com/cloudinary/http5/ApiUtils.java | 3 ++- cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java | 3 ++- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 2 +- .../src/main/java/com/cloudinary/test/MetadataTestHelper.java | 4 +++- .../src/main/java/com/cloudinary/test/helpers/Feature.java | 4 +++- 11 files changed, 28 insertions(+), 11 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java b/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java index bcd8f654..2f20414f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java +++ b/cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java @@ -3,7 +3,9 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; -public class SmartUrlEncoder { +public final class SmartUrlEncoder { + private SmartUrlEncoder() {} + public static String encode(String input) { try { return URLEncoder.encode(input, "UTF-8").replace("%2F", "/").replace("%3A", ":").replace("+", "%20"); diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index c5fcb1f0..f81da3cc 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -8,7 +8,9 @@ import java.security.NoSuchAlgorithmException; import java.util.*; -public class Util { +public final class Util { + private Util() {} + static final String[] BOOLEAN_UPLOAD_OPTIONS = new String[]{"backup", "exif", "faces", "colors", "image_metadata", "use_filename", "unique_filename", "eager_async", "invalidate", "discard_original_filename", "overwrite", "phash", "return_delete_token", "async", "quality_analysis", "cinemagraph_analysis", "accessibility_analysis", "use_filename_as_display_name", "use_asset_folder_as_public_id_prefix", "unique_display_name", "media_metadata", "visual_search", diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Map.java b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Map.java index d5e755f9..f9948974 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Map.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/Base64Map.java @@ -3,7 +3,9 @@ import java.util.HashMap; import java.util.Map; -public class Base64Map { +public final class Base64Map { + private Base64Map() {} + public static Map values; static { diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java b/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java index 2be36583..39ba901e 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/HtmlEscape.java @@ -16,7 +16,8 @@ * this program code. */ -public class HtmlEscape { +public final class HtmlEscape { + private HtmlEscape() {} private static char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java index 437c04db..2dc607f6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/ObjectUtils.java @@ -11,7 +11,9 @@ import java.util.*; -public class ObjectUtils { +public final class ObjectUtils { + private ObjectUtils() {} + /** * Formats a Date as an ISO-8601 string representation. * @param date Date to format diff --git a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java index 0d25bacb..f8a21231 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java +++ b/cloudinary-core/src/main/java/com/cloudinary/utils/StringUtils.java @@ -8,7 +8,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class StringUtils { +public final class StringUtils { + private StringUtils() {} + public static final String EMPTY = ""; /** diff --git a/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiUtils.java b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiUtils.java index af67a1e8..040fd714 100644 --- a/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiUtils.java +++ b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiUtils.java @@ -10,7 +10,8 @@ import java.util.*; -public class ApiUtils { +public final class ApiUtils { + private ApiUtils() {} public static void setTimeouts(HttpUriRequestBase request, Map options) { RequestConfig config = request.getConfig(); diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java b/cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java index a3767492..5c11458f 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java @@ -10,7 +10,8 @@ * * @author jpollak */ -public class Singleton { +public final class Singleton { + private Singleton() {} private static Cloudinary cloudinary; diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index ad89074a..e5ae3703 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -670,7 +670,7 @@ public void testRateLimits() throws Exception { @Test public void testConfiguration() throws Exception { - ApiResponse result = cloudinary.api().configuration(new ObjectUtils().asMap("settings", true)); + ApiResponse result = cloudinary.api().configuration(ObjectUtils.asMap("settings", true)); Map settings = (Map) result.get("settings"); Assert.assertNotNull(settings.get("folder_mode")); } diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java index cdb52487..2a128c7f 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java @@ -6,7 +6,9 @@ import com.cloudinary.metadata.MetadataValidation; import com.cloudinary.metadata.StringMetadataField; -public class MetadataTestHelper { +public final class MetadataTestHelper { + private MetadataTestHelper() {} + public static StringMetadataField newFieldInstance(String label, Boolean mandatory) throws Exception { StringMetadataField field = new StringMetadataField(); field.setLabel(label); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java index 2ced269c..b66bd303 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java @@ -1,6 +1,8 @@ package com.cloudinary.test.helpers; -public class Feature { +public final class Feature { + private Feature() {} + public static final String ALL = "all"; public static final String DYNAMIC_FOLDERS = "dynamic_folders"; public static final String BACKEDUP_ASSETS = "backedup_assets"; From c4d7b2926fe1789237672485b6ab026415ca509f Mon Sep 17 00:00:00 2001 From: Piotr Idzik <65706193+vil02@users.noreply.github.com> Date: Mon, 21 Oct 2024 22:33:00 +0200 Subject: [PATCH 22/40] Remove unused imports --- cloudinary-core/src/main/java/com/cloudinary/AuthToken.java | 3 --- cloudinary-core/src/main/java/com/cloudinary/Search.java | 1 - cloudinary-core/src/main/java/com/cloudinary/Url.java | 1 - .../src/main/java/com/cloudinary/metadata/MetadataRule.java | 1 - .../java/com/cloudinary/strategies/AbstractApiStrategy.java | 5 ----- .../src/test/java/com/cloudinary/AuthTokenTest.java | 2 -- .../src/test/java/com/cloudinary/test/CloudinaryTest.java | 1 - .../src/main/java/com/cloudinary/http5/ApiStrategy.java | 2 -- .../main/java/com/cloudinary/taglib/CloudinaryImageTag.java | 4 ---- .../main/java/com/cloudinary/taglib/CloudinaryVideoTag.java | 1 - .../java/com/cloudinary/test/AbstractAccountApiTest.java | 1 - .../main/java/com/cloudinary/test/AbstractSearchTest.java | 3 --- .../main/java/com/cloudinary/test/AbstractUploaderTest.java | 2 -- .../src/main/java/cloudinary/lib/PhotoUploadValidator.java | 6 ------ .../main/java/cloudinary/controllers/PhotoController.java | 1 - .../src/main/java/cloudinary/lib/PhotoUploadValidator.java | 6 ------ 16 files changed, 40 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java index aa8cf213..a5114dd3 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java +++ b/cloudinary-core/src/main/java/com/cloudinary/AuthToken.java @@ -5,13 +5,10 @@ import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.nio.charset.Charset; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.*; -import java.util.regex.Matcher; import java.util.regex.Pattern; /** diff --git a/cloudinary-core/src/main/java/com/cloudinary/Search.java b/cloudinary-core/src/main/java/com/cloudinary/Search.java index 2ba3ac8b..369830c6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Search.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Search.java @@ -6,7 +6,6 @@ import com.cloudinary.utils.StringUtils; import org.cloudinary.json.JSONObject; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Url.java b/cloudinary-core/src/main/java/com/cloudinary/Url.java index 6517bd28..5365c996 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Url.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Url.java @@ -13,7 +13,6 @@ import java.util.regex.Pattern; import java.util.zip.CRC32; -import com.cloudinary.utils.Analytics; import com.cloudinary.utils.Base64Coder; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.utils.StringUtils; diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRule.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRule.java index 65edbed4..4df82ded 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRule.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRule.java @@ -2,7 +2,6 @@ import com.cloudinary.utils.ObjectUtils; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; diff --git a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java index 8878bf2c..0342f5bc 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java +++ b/cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java @@ -2,12 +2,7 @@ import com.cloudinary.Api; import com.cloudinary.Api.HttpMethod; -import com.cloudinary.SmartUrlEncoder; import com.cloudinary.api.ApiResponse; -import com.cloudinary.utils.Base64Coder; -import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; -import java.util.Arrays; import java.util.Map; diff --git a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java index 468c43bb..49fd8d35 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/AuthTokenTest.java @@ -1,6 +1,5 @@ package com.cloudinary; -import com.cloudinary.utils.Analytics; import com.cloudinary.utils.ObjectUtils; import org.hamcrest.CoreMatchers; @@ -11,7 +10,6 @@ import org.junit.Test; import org.junit.rules.TestName; -import java.io.UnsupportedEncodingException; import java.util.Calendar; import java.util.Collections; import java.util.Map; diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java index 9e436183..b43a2bc8 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -18,7 +18,6 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; -import java.lang.reflect.ParameterizedType; import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; diff --git a/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java index 2852225d..40171489 100644 --- a/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java +++ b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java @@ -7,7 +7,6 @@ import com.cloudinary.http5.api.Response; import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; -import com.cloudinary.utils.StringUtils; import org.apache.hc.client5.http.classic.methods.*; import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; @@ -25,7 +24,6 @@ import java.lang.reflect.Constructor; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.List; import java.util.Map; diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java index 2de25e3b..8b253533 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java @@ -4,12 +4,8 @@ import java.util.HashMap; import java.util.Map; -import javax.servlet.ServletRequest; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; -import javax.servlet.jsp.PageContext; -import javax.servlet.jsp.tagext.DynamicAttributes; -import javax.servlet.jsp.tagext.SimpleTagSupport; import com.cloudinary.*; diff --git a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryVideoTag.java b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryVideoTag.java index 9ed803ee..ec2adb54 100644 --- a/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryVideoTag.java +++ b/cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryVideoTag.java @@ -1,7 +1,6 @@ package com.cloudinary.taglib; import java.io.IOException; -import java.util.HashMap; import java.util.Map; import javax.servlet.jsp.JspException; diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java index 5b8e9633..7852f96b 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractAccountApiTest.java @@ -15,7 +15,6 @@ import static java.util.Collections.singletonMap; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.*; -import static org.junit.Assume.assumeTrue; public abstract class AbstractAccountApiTest extends MockableTest { private static Random rand = new Random(); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java index 5e30d26b..e6bf5d6e 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractSearchTest.java @@ -1,9 +1,7 @@ package com.cloudinary.test; import com.cloudinary.Cloudinary; -import com.cloudinary.Configuration; import com.cloudinary.Search; -import com.cloudinary.api.ApiResponse; import com.cloudinary.utils.ObjectUtils; import org.junit.*; import org.junit.rules.TestName; @@ -13,7 +11,6 @@ import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.core.AllOf.allOf; import static org.junit.Assert.*; import static org.junit.Assume.assumeNotNull; diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java index 19098f6c..5de797fa 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java @@ -1,7 +1,6 @@ package com.cloudinary.test; import com.cloudinary.*; -import com.cloudinary.api.ApiResponse; import com.cloudinary.metadata.StringMetadataField; import com.cloudinary.test.rules.RetryRule; import com.cloudinary.utils.ObjectUtils; @@ -13,7 +12,6 @@ import java.io.*; import java.net.HttpURLConnection; import java.net.URL; -import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; diff --git a/samples/photo_album/src/main/java/cloudinary/lib/PhotoUploadValidator.java b/samples/photo_album/src/main/java/cloudinary/lib/PhotoUploadValidator.java index 2bcdc56d..0a389e02 100644 --- a/samples/photo_album/src/main/java/cloudinary/lib/PhotoUploadValidator.java +++ b/samples/photo_album/src/main/java/cloudinary/lib/PhotoUploadValidator.java @@ -1,16 +1,10 @@ package cloudinary.lib; import cloudinary.models.PhotoUpload; -import com.cloudinary.Cloudinary; -import com.cloudinary.Singleton; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - public class PhotoUploadValidator implements Validator { public boolean supports(Class clazz) { return PhotoUpload.class.equals(clazz); diff --git a/samples/photo_album_gae/src/main/java/cloudinary/controllers/PhotoController.java b/samples/photo_album_gae/src/main/java/cloudinary/controllers/PhotoController.java index 53e8b537..7a6438a8 100644 --- a/samples/photo_album_gae/src/main/java/cloudinary/controllers/PhotoController.java +++ b/samples/photo_album_gae/src/main/java/cloudinary/controllers/PhotoController.java @@ -2,7 +2,6 @@ import cloudinary.lib.PhotoUploadValidator; import cloudinary.models.PhotoUpload; -import com.cloudinary.Cloudinary; import com.cloudinary.utils.ObjectUtils; import com.cloudinary.Singleton; import org.springframework.stereotype.Controller; diff --git a/samples/photo_album_gae/src/main/java/cloudinary/lib/PhotoUploadValidator.java b/samples/photo_album_gae/src/main/java/cloudinary/lib/PhotoUploadValidator.java index 2bcdc56d..0a389e02 100644 --- a/samples/photo_album_gae/src/main/java/cloudinary/lib/PhotoUploadValidator.java +++ b/samples/photo_album_gae/src/main/java/cloudinary/lib/PhotoUploadValidator.java @@ -1,16 +1,10 @@ package cloudinary.lib; import cloudinary.models.PhotoUpload; -import com.cloudinary.Cloudinary; -import com.cloudinary.Singleton; import org.springframework.validation.Errors; import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - public class PhotoUploadValidator implements Validator { public boolean supports(Class clazz) { return PhotoUpload.class.equals(clazz); From 3a0e000b4fd72dd6833e4bbba0500532fb31ed4e Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:00:49 +0200 Subject: [PATCH 23/40] Fix upload preset tests --- cloudinary-core/src/main/java/com/cloudinary/Api.java | 4 ++-- .../src/main/java/com/cloudinary/test/AbstractApiTest.java | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 9dbacde9..d09df997 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -338,7 +338,7 @@ public ApiResponse updateUploadPreset(String name, Map options) throws Exception if (options == null) options = ObjectUtils.emptyMap(); Map params = Util.buildUploadParams(options); Util.clearEmpty(params); - params.putAll(ObjectUtils.only(options, "unsigned", "disallow_public_id", "live")); + params.putAll(ObjectUtils.only(options, "unsigned", "disallow_public_id")); return callApi(HttpMethod.PUT, Arrays.asList("upload_presets", name), params, options); } @@ -346,7 +346,7 @@ public ApiResponse createUploadPreset(Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); Map params = Util.buildUploadParams(options); Util.clearEmpty(params); - params.putAll(ObjectUtils.only(options, "name", "unsigned", "disallow_public_id", "live")); + params.putAll(ObjectUtils.only(options, "name", "unsigned", "disallow_public_id")); return callApi(HttpMethod.POST, Arrays.asList("upload_presets"), params, options); } diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index e5ae3703..9ec8746f 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -825,14 +825,13 @@ public void testGetUploadPreset() throws Exception { String[] tags = {"a", "b", "c"}; Map context = ObjectUtils.asMap("a", "b", "c", "d"); Map result = api.createUploadPreset(ObjectUtils.asMap("unsigned", true, "folder", "folder", "transformation", EXPLICIT_TRANSFORMATION, "tags", tags, "context", - context, "live", true, "use_asset_folder_as_public_id_prefix", true)); + context, "use_asset_folder_as_public_id_prefix", true)); String name = result.get("name").toString(); Map preset = api.uploadPreset(name, ObjectUtils.emptyMap()); assertEquals(preset.get("name"), name); assertEquals(Boolean.TRUE, preset.get("unsigned")); Map settings = (Map) preset.get("settings"); assertEquals(settings.get("folder"), "folder"); - assertEquals(settings.get("live"), Boolean.TRUE); assertEquals(settings.get("use_asset_folder_as_public_id_prefix"), true); Map outTransformation = (Map) ((java.util.ArrayList) settings.get("transformation")).get(0); assertEquals(outTransformation.get("width"), 100); @@ -866,13 +865,12 @@ public void testUpdateUploadPreset() throws Exception { String name = api.createUploadPreset(ObjectUtils.asMap("folder", "folder")).get("name").toString(); Map preset = api.uploadPreset(name, ObjectUtils.emptyMap()); Map settings = (Map) preset.get("settings"); - settings.putAll(ObjectUtils.asMap("colors", true, "unsigned", true, "disallow_public_id", true, "live", true, "eval",AbstractUploaderTest.SRC_TEST_EVAL)); + settings.putAll(ObjectUtils.asMap("colors", true, "unsigned", true, "disallow_public_id", true, "eval",AbstractUploaderTest.SRC_TEST_EVAL)); api.updateUploadPreset(name, settings); settings.remove("unsigned"); preset = api.uploadPreset(name, ObjectUtils.emptyMap()); assertEquals(name, preset.get("name")); assertEquals(Boolean.TRUE, preset.get("unsigned")); - assertEquals(settings.get("live"), Boolean.TRUE); assertEquals(settings, preset.get("settings")); api.deleteUploadPreset(name, ObjectUtils.emptyMap()); From 0f456e8a14b2f3957bc8fdd75373446bd6927144 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:51:41 +0200 Subject: [PATCH 24/40] Update Cloudinary constructor --- .../main/java/com/cloudinary/Cloudinary.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 9bdbe122..b802d26d 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -85,22 +85,21 @@ private void loadStrategies() { } public Cloudinary(Map config) { - this.config = new Configuration(config); - loadStrategies(); + this(new Configuration(config)); } public Cloudinary(String cloudinaryUrl) { - this.config = Configuration.from(cloudinaryUrl); - loadStrategies(); + this(Configuration.from(cloudinaryUrl)); } public Cloudinary() { - String cloudinaryUrl = System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")); - if (cloudinaryUrl != null) { - this.config = Configuration.from(cloudinaryUrl); - } else { - this.config = new Configuration(); - } + this(System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL")) != null + ? Configuration.from(System.getProperty("CLOUDINARY_URL", System.getenv("CLOUDINARY_URL"))) + : new Configuration()); + } + + public Cloudinary(Configuration config) { + this.config = config; loadStrategies(); } From fad1ed50956b21206706750c06f5f4d38c5cb82d Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:52:01 +0200 Subject: [PATCH 25/40] Fix and test register upload + api strategies --- .../src/main/java/com/cloudinary/Cloudinary.java | 6 +++--- .../java/com/cloudinary/test/CloudinaryTest.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index b802d26d..f70f8965 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -20,7 +20,7 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public class Cloudinary { - private static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList( + public static List UPLOAD_STRATEGIES = new ArrayList(Arrays.asList( "com.cloudinary.android.UploaderStrategy", "com.cloudinary.http5.UploaderStrategy")); public static List API_STRATEGIES = new ArrayList(Arrays.asList( @@ -59,14 +59,14 @@ public SearchFolders searchFolders() { public static void registerUploaderStrategy(String className) { if (!UPLOAD_STRATEGIES.contains(className)) { - UPLOAD_STRATEGIES.add(className); + UPLOAD_STRATEGIES.add(0, className); } } public static void registerAPIStrategy(String className) { if (!API_STRATEGIES.contains(className)) { - API_STRATEGIES.add(className); + API_STRATEGIES.add(0, className); } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java index b43a2bc8..18064976 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1464,6 +1464,20 @@ public void testDownloadBackedupAsset() throws UnsupportedEncodingException, URI assertNotNull(params.get("timestamp")); } + @Test + public void testRegisterUploaderStrategy() { + String className = "myUploadStrategy"; + Cloudinary.registerUploaderStrategy(className); + assertEquals(className, Cloudinary.UPLOAD_STRATEGIES.get(0)); + } + + @Test + public void testRegisterApiStrategy() { + String className = "myApiStrategy"; + Cloudinary.registerAPIStrategy(className); + assertEquals(className, Cloudinary.API_STRATEGIES.get(0)); + } + private void assertFieldsEqual(Object a, Object b) throws IllegalAccessException { assertEquals("Two objects must be the same class", a.getClass(), b.getClass()); Field[] fields = a.getClass().getFields(); From 98f0260ce1b4e0fc965ce16c09e8956a35a2e022 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 20 Jan 2025 08:10:00 +0200 Subject: [PATCH 26/40] Fix Http client 5.0 init (#369) --- .../com/cloudinary/http5/ApiStrategy.java | 38 +++++++++++++++++-- .../java/com/cloudinary/test/ApiTest.java | 38 ++++++++++++++++++- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java index 40171489..9c0145e9 100644 --- a/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java +++ b/cloudinary-http5/src/main/java/com/cloudinary/http5/ApiStrategy.java @@ -8,15 +8,20 @@ import com.cloudinary.strategies.AbstractApiStrategy; import com.cloudinary.utils.ObjectUtils; import org.apache.hc.client5.http.classic.methods.*; +import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.net.URIBuilder; +import org.apache.hc.core5.util.Timeout; import org.cloudinary.json.JSONException; import org.cloudinary.json.JSONObject; @@ -36,15 +41,42 @@ public class ApiStrategy extends AbstractApiStrategy { private CloseableHttpClient client; - @Override public void init(Api api) { super.init(api); - this.client = HttpClients.custom() - .setUserAgent(this.api.cloudinary.getUserAgent() + " ApacheHttpClient/" + APACHE_HTTP_CLIENT_VERSION) + HttpClientBuilder clientBuilder = HttpClients.custom(); + clientBuilder.useSystemProperties().setUserAgent(this.api.cloudinary.getUserAgent() + " ApacheHttpClient/" + APACHE_HTTP_CLIENT_VERSION); + + HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) api.cloudinary.config.properties.get("connectionManager"); + if (connectionManager != null) { + clientBuilder.setConnectionManager(connectionManager); + } + + RequestConfig requestConfig = buildRequestConfig(); + + client = clientBuilder + .setDefaultRequestConfig(requestConfig) .build(); } + public RequestConfig buildRequestConfig() { + RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); + + if (api.cloudinary.config.proxyHost != null && api.cloudinary.config.proxyPort != 0) { + HttpHost proxy = new HttpHost(api.cloudinary.config.proxyHost, api.cloudinary.config.proxyPort); + requestConfigBuilder.setProxy(proxy); + } + + int timeout = this.api.cloudinary.config.timeout; + if (timeout > 0) { + requestConfigBuilder.setResponseTimeout(Timeout.ofSeconds(timeout)) + .setConnectionRequestTimeout(Timeout.ofSeconds(timeout)) + .setConnectTimeout(Timeout.ofSeconds(timeout)); + } + + return requestConfigBuilder.build(); + } + @SuppressWarnings({"rawtypes", "unchecked"}) public ApiResponse callApi(Api.HttpMethod method, String apiUrl, Map params, Map options, String autorizationHeader) throws Exception { HttpUriRequestBase request = prepareRequest(method, apiUrl, params, options); diff --git a/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java index a2f3c89f..4735d89f 100644 --- a/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java @@ -1,15 +1,50 @@ package com.cloudinary.test; +import com.cloudinary.Cloudinary; import com.cloudinary.api.ApiResponse; +import com.cloudinary.http5.ApiStrategy; import com.cloudinary.utils.ObjectUtils; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.util.Timeout; import org.junit.Test; import org.junit.experimental.categories.Category; + import java.util.Map; public class ApiTest extends AbstractApiTest { + @Test + public void testBuildRequestConfig_withProxyAndTimeout() { + Cloudinary cloudinary = new Cloudinary("cloudinary://test:test@test.com"); + cloudinary.config.proxyHost = "127.0.0.1"; + cloudinary.config.proxyPort = 8080; + cloudinary.config.timeout = 15; + + RequestConfig requestConfig = ((ApiStrategy)cloudinary.api().getStrategy()).buildRequestConfig(); + + assert(requestConfig.getProxy() != null); + HttpHost proxy = requestConfig.getProxy(); + assert("127.0.0.1" == proxy.getHostName()); + assert(8080 == proxy.getPort()); + + assert(15000 == requestConfig.getConnectionRequestTimeout().toMilliseconds()); + assert(15000 == requestConfig.getResponseTimeout().toMilliseconds()); + } + + @Test + public void testBuildRequestConfig_withoutProxy() { + Cloudinary cloudinary = new Cloudinary("cloudinary://test:test@test.com"); + cloudinary.config.timeout = 10; + + RequestConfig requestConfig = ((ApiStrategy)cloudinary.api().getStrategy()).buildRequestConfig(); + + assert(requestConfig.getProxy() == null); + assert(10000 == requestConfig.getConnectionRequestTimeout().toMilliseconds()); + assert(10000 == requestConfig.getResponseTimeout().toMilliseconds()); + } + @Category(TimeoutTest.class) @Test(expected = Exception.class) public void testConnectTimeoutParameter() throws Exception { @@ -41,5 +76,4 @@ public void testTimeoutParameter() throws Exception { throw new Exception("Socket timeout"); } } -} - +} \ No newline at end of file From 209037e99016bb76067a52cea33e2098502ac868 Mon Sep 17 00:00:00 2001 From: Adi Mizrahi Date: Mon, 20 Jan 2025 12:17:20 +0200 Subject: [PATCH 27/40] Version 2.1.0 --- CHANGELOG.md | 8 ++++++++ README.md | 4 ++-- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc13fee5..ffe151ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +2.1.0 / 2025-01-20 +================== + +* Fix Http client proxy +* Fix Http client system properties support +* Add Cloudinary constructor for `Configuration` +* Fix Register strategy functions + 2.0.0 / 2024-09-29 ================== diff --git a/README.md b/README.md index 4e44ba97..74e77daa 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ For the complete documentation, see the [Java SDK Guide](https://cloudinary.com/ | SDK Version | Java 6+ | Java 8 | |----------------|---------|--------| | 1.1.0 - 1.39.0 | V | | -| 2.0.0 | | V | +| 2.0.0+ | | V | @@ -39,7 +39,7 @@ The cloudinary_java library is available in [Maven Central](https://mvnrepositor com.cloudinary cloudinary-http45 - 2.0.0 + 2.1.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index f70f8965..aa156ec2 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -32,7 +32,7 @@ public class Cloudinary { public final static String AKAMAI_SHARED_CDN = "res.cloudinary.com"; public final static String SHARED_CDN = AKAMAI_SHARED_CDN; - public final static String VERSION = "2.0.0"; + public final static String VERSION = "2.1.0"; static String USER_AGENT_PREFIX = "CloudinaryJava"; public final static String USER_AGENT_JAVA_VERSION = "(Java " + System.getProperty("java.version") + ")"; diff --git a/gradle.properties b/gradle.properties index c15a7578..6ce82a60 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=2.0.0 +version=2.1.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 4e7d91f77ae8f4a9c45e6439dac39caafde72815 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 27 Jan 2025 08:06:28 +0200 Subject: [PATCH 28/40] Add delete resources by assetids --- .../src/main/java/com/cloudinary/Api.java | 7 +++++++ .../java/com/cloudinary/test/AbstractApiTest.java | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index d09df997..0885ab13 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -240,6 +240,13 @@ public ApiResponse deleteResources(Iterable publicIds, Map options) thro return callApi(HttpMethod.DELETE, Arrays.asList("resources", resourceType, type), params, options); } + public ApiResponse deleteResourcesByAssetIds(Iterable assetIds, Map options) throws Exception { + if (options == null) options = ObjectUtils.emptyMap(); + Map params = ObjectUtils.only(options, "keep_original", "invalidate", "next_cursor", "transformations"); + params.put("asset_ids", assetIds); + return callApi(HttpMethod.DELETE, Arrays.asList("resources"), params, options); + } + public ApiResponse deleteDerivedByTransformation(Iterable publicIds, List transformations, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index 9ec8746f..4a73ff73 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -464,6 +464,20 @@ public void test09DeleteResources() throws Exception { api.resource(public_id, ObjectUtils.emptyMap()); } + @Test(expected = NotFound.class) + public void test10DeleteResourcesByAssetsIds() throws Exception { + String public_id = "api_,test4" + SUFFIX; + cloudinary.uploader().upload(SRC_TEST_IMAGE, ObjectUtils.asMap("public_id", public_id, "tags", UPLOAD_TAGS)); + Map resource = api.resource(public_id, ObjectUtils.emptyMap()); + assertNotNull(resource); + String assetId = (String)resource.get("asset_id"); + ApiResponse response = api.deleteResourcesByAssetIds(Arrays.asList(assetId), ObjectUtils.emptyMap()); + assertNotNull(response); + assertNotNull(response.get("deleted")); + assertNotNull(response.get("deleted_counts")); + api.resource(public_id, ObjectUtils.emptyMap()); + } + @Test(expected = NotFound.class) public void test09aDeleteResourcesByPrefix() throws Exception { // should allow deleting resources From a8284ebc8f0ae98760405d6454706e97a341529f Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:15:18 +0200 Subject: [PATCH 29/40] Add allow dynamic list to List Metadata field --- .../java/com/cloudinary/metadata/MetadataField.java | 7 +++++++ .../test/AbstractStructuredMetadataTest.java | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java index 4f3bdf33..7fe81c43 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java +++ b/cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataField.java @@ -18,6 +18,7 @@ public class MetadataField extends JSONObject { public static final String VALIDATION = "validation"; public static final String RESTRICTIONS = "restrictions"; public static final String DEFAULT_DISABLED = "default_disabled"; + public static final String ALLOW_DYNAMIC_LIST_VALUES = "allow_dynamic_list_values"; public MetadataField(MetadataFieldType type) { put(TYPE, type.toString()); @@ -148,4 +149,10 @@ public void setRestrictions(Restrictions restrictions) { public void setDefaultDisabled(Boolean disabled) { put(DEFAULT_DISABLED, disabled); } + + /** + * Set the value indicating whether the dynamic list values should allow + * @param allowDynamicListValues The value to set. + */ + public void setAllowDynamicListValues(Boolean allowDynamicListValues) {put(ALLOW_DYNAMIC_LIST_VALUES, allowDynamicListValues);} } \ No newline at end of file diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java index 241ac47d..60467685 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java @@ -73,6 +73,15 @@ public void testCreateMetadata() throws Exception { assertEquals(setField.getLabel(), result.get("label")); } + @Test + public void testCreateSetMetadataWithAllowDynamicListValues() throws Exception { + SetMetadataField setField = createSetField("testCreateMetadata_2"); + ApiResponse result = cloudinary.api().addMetadataField(setField); + assertNotNull(result); + assertEquals(setField.getLabel(), result.get("label")); + assertEquals(true, result.get("allow_dynamic_list_values")); + } + @Test public void testFieldRestrictions() throws Exception { StringMetadataField stringField = newFieldInstance("testCreateMetadata_3", true); @@ -367,6 +376,7 @@ private SetMetadataField createSetField(String labelPrefix) { String label = labelPrefix + "_" + SUFFIX; setField.setLabel(label); setField.setMandatory(false); + setField.setAllowDynamicListValues(true); setField.setValidation(new MetadataValidation.StringLength(3, 99)); setField.setDefaultValue(Arrays.asList("id2", "id3")); setField.setValidation(null); From 61a5c905e84a24af5e328268b8444e7290b2f27e Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:42:59 +0200 Subject: [PATCH 30/40] Add restore by asset ids api call --- .../src/main/java/com/cloudinary/Api.java | 8 ++++++ .../com/cloudinary/test/AbstractApiTest.java | 26 +++++++++++++++++++ .../test/AbstractStructuredMetadataTest.java | 2 +- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 0885ab13..0cbab208 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -393,6 +393,14 @@ public ApiResponse restore(Iterable publicIds, Map options) throws Excep return response; } + public ApiResponse restoreByAssetIds(Iterable assetIds, Map options) throws Exception { + if (options == null) + options = ObjectUtils.emptyMap(); + Map params = new HashMap(); + params.put("asset_ids", assetIds); + return callApi(HttpMethod.POST, Arrays.asList("resources", "restore"), params, options); + } + public ApiResponse uploadMappings(Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index 4a73ff73..5c854d75 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -971,6 +971,32 @@ public void testRestore() throws Exception { assertEquals(resource.get("bytes"), 3381); } + @Test + public void testRestoreByAssetIds() throws Exception { + + // Upload + cloudinary.uploader().upload(SRC_TEST_IMAGE, + ObjectUtils.asMap("public_id", API_TEST_RESTORE, "backup", true, "tags", UPLOAD_TAGS)); + Map resource = api.resource(API_TEST_RESTORE, ObjectUtils.emptyMap()); + assertEquals(resource.get("bytes"), 3381); + + //Delete + api.deleteResources(Collections.singletonList(API_TEST_RESTORE), ObjectUtils.emptyMap()); + resource = api.resource(API_TEST_RESTORE, ObjectUtils.emptyMap()); + String assetId = (String) resource.get("asset_id"); + assertEquals(resource.get("bytes"), 0); + assertNotNull(assetId); + assertTrue((Boolean) resource.get("placeholder")); + + //Restore + Map response = api.restoreByAssetIds(Collections.singletonList(assetId), ObjectUtils.emptyMap()); + Map info = (Map) response.get(assetId); + assertNotNull(info); + assertEquals(info.get("bytes"), 3381); + resource = api.resource(API_TEST_RESTORE, ObjectUtils.emptyMap()); + assertEquals(resource.get("bytes"), 3381); + } + @Test public void testRestoreDifferentVersionsOfDeletedAsset() throws Exception { final String TEST_RESOURCE_PUBLIC_ID = "api_test_restore_different_versions_single_asset" + SUFFIX; diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java index 60467685..b1137fb4 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractStructuredMetadataTest.java @@ -75,7 +75,7 @@ public void testCreateMetadata() throws Exception { @Test public void testCreateSetMetadataWithAllowDynamicListValues() throws Exception { - SetMetadataField setField = createSetField("testCreateMetadata_2"); + SetMetadataField setField = createSetField("testCreateMetadata_4"); ApiResponse result = cloudinary.api().addMetadataField(setField); assertNotNull(result); assertEquals(setField.getLabel(), result.get("label")); From c646ad486e1beb156e94da729da9da3550bbe891 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Thu, 30 Jan 2025 14:36:53 +0200 Subject: [PATCH 31/40] Fix Uploader strategy --- .../cloudinary/http5/UploaderStrategy.java | 37 ++++++++++++++++++- .../java/com/cloudinary/test/ApiTest.java | 27 +++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/cloudinary-http5/src/main/java/com/cloudinary/http5/UploaderStrategy.java b/cloudinary-http5/src/main/java/com/cloudinary/http5/UploaderStrategy.java index 5f87cbd8..589dff5b 100644 --- a/cloudinary-http5/src/main/java/com/cloudinary/http5/UploaderStrategy.java +++ b/cloudinary-http5/src/main/java/com/cloudinary/http5/UploaderStrategy.java @@ -8,16 +8,21 @@ import com.cloudinary.utils.StringUtils; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; +import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.mime.ByteArrayBody; import org.apache.hc.client5.http.entity.mime.FileBody; import org.apache.hc.client5.http.entity.mime.HttpMultipartMode; import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.apache.hc.core5.util.Timeout; import java.io.File; import java.io.IOException; @@ -35,11 +40,39 @@ public class UploaderStrategy extends AbstractUploaderStrategy { public void init(Uploader uploader) { super.init(uploader); - this.client = HttpClients.custom() - .setUserAgent(cloudinary().getUserAgent() + " ApacheHttpClient/" + APACHE_HTTP_CLIENT_VERSION) + HttpClientBuilder clientBuilder = HttpClients.custom(); + clientBuilder.useSystemProperties().setUserAgent(cloudinary().getUserAgent() + " ApacheHttpClient/" + APACHE_HTTP_CLIENT_VERSION); + + HttpClientConnectionManager connectionManager = (HttpClientConnectionManager) cloudinary().config.properties.get("connectionManager"); + if (connectionManager != null) { + clientBuilder.setConnectionManager(connectionManager); + } + + RequestConfig requestConfig = buildRequestConfig(); + + client = clientBuilder + .setDefaultRequestConfig(requestConfig) .build(); } + public RequestConfig buildRequestConfig() { + RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); + + if (cloudinary().config.proxyHost != null && cloudinary().config.proxyPort != 0) { + HttpHost proxy = new HttpHost(cloudinary().config.proxyHost, cloudinary().config.proxyPort); + requestConfigBuilder.setProxy(proxy); + } + + int timeout = cloudinary().config.timeout; + if (timeout > 0) { + requestConfigBuilder.setResponseTimeout(Timeout.ofSeconds(timeout)) + .setConnectionRequestTimeout(Timeout.ofSeconds(timeout)) + .setConnectTimeout(Timeout.ofSeconds(timeout)); + } + + return requestConfigBuilder.build(); + } + @SuppressWarnings({"rawtypes", "unchecked"}) @Override public Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException { diff --git a/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java b/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java index 4735d89f..53da8866 100644 --- a/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java +++ b/cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java @@ -11,6 +11,9 @@ import org.junit.experimental.categories.Category; import java.util.Map; +import java.util.UUID; + +import static com.cloudinary.utils.ObjectUtils.asMap; public class ApiTest extends AbstractApiTest { @@ -48,7 +51,7 @@ public void testBuildRequestConfig_withoutProxy() { @Category(TimeoutTest.class) @Test(expected = Exception.class) public void testConnectTimeoutParameter() throws Exception { - Map options = ObjectUtils.asMap( + Map options = asMap( "max_results", 500, "connect_timeout", 0.2); @@ -65,7 +68,7 @@ public void testConnectTimeoutParameter() throws Exception { @Test(expected = Exception.class) public void testTimeoutParameter() throws Exception { // Set a very short request timeout to trigger a timeout exception - Map options = ObjectUtils.asMap( + Map options = asMap( "max_results", 500, "timeout", Timeout.ofMilliseconds(1000)); // Set the timeout to 1 second @@ -76,4 +79,24 @@ public void testTimeoutParameter() throws Exception { throw new Exception("Socket timeout"); } } + + @Category(TimeoutTest.class) + @Test(expected = Exception.class) + public void testUploaderTimeoutParameter() throws Exception { + Cloudinary cloudinary = new Cloudinary("cloudinary://test:test@test.com"); + cloudinary.config.uploadPrefix = "https://10.255.255.1"; + String publicId = UUID.randomUUID().toString(); + // Set a very short request timeout to trigger a timeout exception + Map options = asMap( + "max_results", 500, + "timeout", Timeout.ofMilliseconds(10)); // Set the timeout to 1 second + + try { + Map result = cloudinary.uploader().addContext(asMap("caption", "new caption"), new String[]{publicId, "no-such-id"}, options); + } catch (Exception e) { + // Convert IOException to SocketTimeoutException if appropriate + throw new Exception("Socket timeout"); + } + } + } \ No newline at end of file From a9e77cd2ee94b4ec8562f8c5a731accad64c0134 Mon Sep 17 00:00:00 2001 From: Adi Mizrahi Date: Sun, 2 Feb 2025 14:15:48 +0200 Subject: [PATCH 32/40] Version 2.2.0 --- CHANGELOG.md | 8 ++++++++ README.md | 2 +- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffe151ac..ba03059e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +2.2.0 / 2025-02-02 +================== + +* Fix Uploader strategy +* Add restore assets by asset ids +* Add allow dynamic list parameter +* Add delete resources by asset ids + 2.1.0 / 2025-01-20 ================== diff --git a/README.md b/README.md index 74e77daa..f5659de0 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ The cloudinary_java library is available in [Maven Central](https://mvnrepositor com.cloudinary cloudinary-http45 - 2.1.0 + 2.2.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index aa156ec2..19c0c3b8 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -32,7 +32,7 @@ public class Cloudinary { public final static String AKAMAI_SHARED_CDN = "res.cloudinary.com"; public final static String SHARED_CDN = AKAMAI_SHARED_CDN; - public final static String VERSION = "2.1.0"; + public final static String VERSION = "2.2.0"; static String USER_AGENT_PREFIX = "CloudinaryJava"; public final static String USER_AGENT_JAVA_VERSION = "(Java " + System.getProperty("java.version") + ")"; diff --git a/gradle.properties b/gradle.properties index 6ce82a60..aaea4f1d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=2.1.0 +version=2.2.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 45a218aeafd01b273d183b21264be7d8f69d1576 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Mon, 3 Feb 2025 15:39:28 +0200 Subject: [PATCH 33/40] Add skip backup parameter to delete folder api --- .../src/main/java/com/cloudinary/Api.java | 4 +++- .../com/cloudinary/test/AbstractFoldersApiTest.java | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 0cbab208..4a01933f 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -660,8 +660,10 @@ public ApiResponse updateResourcesAccessModeByTag(String accessMode, String tag, * @throws Exception When the folder isn't empty or doesn't exist. */ public ApiResponse deleteFolder(String folder, Map options) throws Exception { + if (options == null || options.isEmpty()) options = ObjectUtils.asMap(); List uri = Arrays.asList("folders", folder); - return callApi(HttpMethod.DELETE, uri, Collections.emptyMap(), options); + Map params = ObjectUtils.only(options, "skip_backup"); + return callApi(HttpMethod.DELETE, uri, params, options); } /** diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractFoldersApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractFoldersApiTest.java index a0478bad..a8835046 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractFoldersApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractFoldersApiTest.java @@ -85,4 +85,17 @@ public void testSubFolderWithParams() throws Exception { ApiResponse result = api.deleteFolder(rootFolderName, null); assertTrue(((List) result.get("deleted")).contains(rootFolderName)); } + + @Test + public void testDeleteFolderWithSkipBackup() throws Exception { + //Create + String rootFolderName = "deleteFolderWithSkipBackup" + SUFFIX; + assertTrue((Boolean) api.createFolder(rootFolderName, null).get("success")); + + //Delete + ApiResponse result = api.deleteFolder(rootFolderName, ObjectUtils.asMap("skip_backup", "true")); + assertTrue(((List) result.get("deleted")).contains(rootFolderName)); + + + } } From bae86bc9717c8ef8f09d28c58de8966c72232540 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 5 Feb 2025 13:00:49 +0200 Subject: [PATCH 34/40] Fix build single resource params --- .../src/main/java/com/cloudinary/Api.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Api.java b/cloudinary-core/src/main/java/com/cloudinary/Api.java index 4a01933f..c84be0d6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Api.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Api.java @@ -162,7 +162,7 @@ public ApiResponse resourceByAssetID(String assetId, Map options) throws Excepti if(options.get("fields") != null) { options.put("fields", StringUtils.join(ObjectUtils.asArray(options.get("fields")), ",")); } - Map params = ObjectUtils.only(options, "tags", "context", "moderations", "fields"); + Map params = buildResourceDetailParams(options); ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", assetId), params, options); return response; } @@ -209,15 +209,19 @@ public ApiResponse resource(String public_id, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); String type = ObjectUtils.asString(options.get("type"), "upload"); + Map params = buildResourceDetailParams(options); - ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type, public_id), - ObjectUtils.only(options, "exif", "colors", "faces", "coordinates", - "image_metadata", "pages", "phash", "max_results", "quality_analysis", "cinemagraph_analysis", - "accessibility_analysis", "versions", "media_metadata", "derived_next_cursor"), options); + ApiResponse response = callApi(HttpMethod.GET, Arrays.asList("resources", resourceType, type, public_id), params, options); return response; } + private Map buildResourceDetailParams(Map options) { + return ObjectUtils.only(options, "exif", "colors", "faces", "coordinates", + "image_metadata", "pages", "phash", "max_results", "quality_analysis", "cinemagraph_analysis", + "accessibility_analysis", "versions", "media_metadata", "derived_next_cursor"); + } + public ApiResponse update(String public_id, Map options) throws Exception { if (options == null) options = ObjectUtils.emptyMap(); String resourceType = ObjectUtils.asString(options.get("resource_type"), "image"); From 90cc3598c6f2093838dff96def7f2337f5675fcc Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 18 Jun 2025 09:09:23 +0300 Subject: [PATCH 35/40] Fix API parameters signature --- .../main/java/com/cloudinary/Cloudinary.java | 6 +-- .../java/com/cloudinary/Configuration.java | 15 ++++++ .../src/main/java/com/cloudinary/Util.java | 52 +++++++++++++------ .../signing/ApiResponseSignatureVerifier.java | 2 +- .../com/cloudinary/test/CloudinaryTest.java | 4 +- .../com/cloudinary/test/AbstractApiTest.java | 35 +++++++++++++ .../cloudinary/test/AbstractUploaderTest.java | 10 ++-- 7 files changed, 97 insertions(+), 27 deletions(-) diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 19c0c3b8..bbb07e36 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -130,8 +130,8 @@ public String signedPreloadedImage(Map result) { + (result.containsKey("format") ? "." + result.get("format") : "") + "#" + result.get("signature"); } - public String apiSignRequest(Map paramsToSign, String apiSecret) { - return Util.produceSignature(paramsToSign, apiSecret, config.signatureAlgorithm); + public String apiSignRequest(Map paramsToSign, String apiSecret, int signatureVersion) { + return Util.produceSignature(paramsToSign, apiSecret, config.signatureAlgorithm, signatureVersion); } /** @@ -206,7 +206,7 @@ public void signRequest(Map params, Map options) if (apiSecret == null) throw new IllegalArgumentException("Must supply api_secret"); Util.clearEmpty(params); - params.put("signature", this.apiSignRequest(params, apiSecret)); + params.put("signature", this.apiSignRequest(params, apiSecret, this.config.signatureVersion)); params.put("api_key", apiKey); } diff --git a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java index 07280d89..7586ae46 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Configuration.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Configuration.java @@ -21,6 +21,7 @@ public class Configuration { public final static String USER_AGENT = "cld-android-" + VERSION; public static final boolean DEFAULT_IS_LONG_SIGNATURE = false; public static final SignatureAlgorithm DEFAULT_SIGNATURE_ALGORITHM = SignatureAlgorithm.SHA1; + public static final int DEFAULT_SIGNATURE_VERSION = 2; private static final String CONFIG_PROP_SIGNATURE_ALGORITHM = "signature_algorithm"; @@ -48,6 +49,7 @@ public class Configuration { public boolean forceVersion = true; public boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; public SignatureAlgorithm signatureAlgorithm = DEFAULT_SIGNATURE_ALGORITHM; + public int signatureVersion = DEFAULT_SIGNATURE_VERSION; public String oauthToken = null; public Boolean analytics; public Configuration() { @@ -75,6 +77,7 @@ private Configuration( boolean forceVersion, boolean longUrlSignature, SignatureAlgorithm signatureAlgorithm, + int signatureVersion, String oauthToken, boolean analytics) { this.cloudName = cloudName; @@ -98,6 +101,7 @@ private Configuration( this.forceVersion = forceVersion; this.longUrlSignature = longUrlSignature; this.signatureAlgorithm = signatureAlgorithm; + this.signatureVersion = signatureVersion; this.oauthToken = oauthToken; this.analytics = analytics; } @@ -140,6 +144,7 @@ public void update(Map config) { } this.longUrlSignature = ObjectUtils.asBoolean(config.get("long_url_signature"), DEFAULT_IS_LONG_SIGNATURE); this.signatureAlgorithm = SignatureAlgorithm.valueOf(ObjectUtils.asString(config.get(CONFIG_PROP_SIGNATURE_ALGORITHM), DEFAULT_SIGNATURE_ALGORITHM.name())); + this.signatureVersion = ObjectUtils.asInteger(config.get("signature_version"), DEFAULT_SIGNATURE_VERSION); this.oauthToken = (String) config.get("oauth_token"); } @@ -173,6 +178,7 @@ public Map asMap() { map.put("properties", new HashMap(properties)); map.put("long_url_signature", longUrlSignature); map.put(CONFIG_PROP_SIGNATURE_ALGORITHM, signatureAlgorithm.toString()); + map.put("signature_version", signatureVersion); map.put("oauth_token", oauthToken); map.put("analytics", analytics); return map; @@ -206,6 +212,7 @@ public Configuration(Configuration other) { this.properties.putAll(other.properties); this.longUrlSignature = other.longUrlSignature; this.signatureAlgorithm = other.signatureAlgorithm; + this.signatureVersion = other.signatureVersion; this.oauthToken = other.oauthToken; this.analytics = other.analytics; } @@ -320,6 +327,7 @@ public static class Builder { private boolean forceVersion = true; private boolean longUrlSignature = DEFAULT_IS_LONG_SIGNATURE; private SignatureAlgorithm signatureAlgorithm = DEFAULT_SIGNATURE_ALGORITHM; + private int signatureVersion = DEFAULT_SIGNATURE_VERSION; private String oauthToken = null; private boolean analytics; @@ -360,6 +368,7 @@ public Configuration build() { forceVersion, longUrlSignature, signatureAlgorithm, + signatureVersion, oauthToken, analytics); configuration.clientHints = clientHints; @@ -500,6 +509,11 @@ public Builder setSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) { return this; } + public Builder setSignatureVersion(int signatureVersion) { + this.signatureVersion = signatureVersion; + return this; + } + public Builder setOAuthToken(String oauthToken) { this.oauthToken = oauthToken; return this; @@ -535,6 +549,7 @@ public Builder from(Configuration other) { this.forceVersion = other.forceVersion; this.longUrlSignature = other.longUrlSignature; this.signatureAlgorithm = other.signatureAlgorithm; + this.signatureVersion = other.signatureVersion; this.oauthToken = other.oauthToken; this.analytics = other.analytics; return this; diff --git a/cloudinary-core/src/main/java/com/cloudinary/Util.java b/cloudinary-core/src/main/java/com/cloudinary/Util.java index f81da3cc..4f15c220 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Util.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Util.java @@ -366,8 +366,8 @@ public static byte[] getUTF8Bytes(String string) { * @param apiSecret secret value * @return hex-string representation of signature calculated based on provided parameters map and secret */ - public static String produceSignature(Map paramsToSign, String apiSecret) { - return produceSignature(paramsToSign, apiSecret, SignatureAlgorithm.SHA1); + public static String produceSignature(Map paramsToSign, String apiSecret, int signatureVersion) { + return produceSignature(paramsToSign, apiSecret, SignatureAlgorithm.SHA1, signatureVersion); } /** @@ -384,22 +384,42 @@ public static String produceSignature(Map paramsToSign, String a * @param signatureAlgorithm type of hashing algorithm to use for calculation of HMAC * @return hex-string representation of signature calculated based on provided parameters map and secret */ - public static String produceSignature(Map paramsToSign, String apiSecret, SignatureAlgorithm signatureAlgorithm) { - Collection params = new ArrayList(); - for (Map.Entry param : new TreeMap(paramsToSign).entrySet()) { - if (param.getValue() instanceof Collection) { - params.add(param.getKey() + "=" + StringUtils.join((Collection) param.getValue(), ",")); - } else if (param.getValue() instanceof Object[]) { - params.add(param.getKey() + "=" + StringUtils.join((Object[]) param.getValue(), ",")); - } else { - if (StringUtils.isNotBlank(param.getValue())) { - params.add(param.getKey() + "=" + param.getValue().toString()); - } + public static String produceSignature(Map paramsToSign, String apiSecret, SignatureAlgorithm signatureAlgorithm, int signatureVersion) { + Collection flattenedParams = flattenAndSanitizeParams(paramsToSign, signatureVersion); + String toSign = StringUtils.join(flattenedParams, "&") + apiSecret; + byte[] hash = Util.hash(toSign, signatureAlgorithm); + return StringUtils.encodeHexString(hash); + } + + private static Collection flattenAndSanitizeParams(Map paramsToSign, int signatureVersion) { + Collection params = new ArrayList<>(); + + for (Map.Entry entry : new TreeMap<>(paramsToSign).entrySet()) { + Object value = entry.getValue(); + String rawValue = null; + + if (value instanceof Collection) { + rawValue = StringUtils.join((Collection) value, ","); + } else if (value instanceof Object[]) { + rawValue = StringUtils.join((Object[]) value, ","); + } else if (value != null && StringUtils.isNotBlank(value.toString())) { + rawValue = value.toString(); + } + + if (rawValue != null) { + String sanitizedValue = (signatureVersion == 2) + ? escapeAmpersand(rawValue) + : rawValue; + + params.add(entry.getKey() + "=" + sanitizedValue); } } - String to_sign = StringUtils.join(params, "&"); - byte[] hash = Util.hash(to_sign + apiSecret, signatureAlgorithm); - return StringUtils.encodeHexString(hash); + + return params; + } + + private static String escapeAmpersand(String input) { + return input.replace("&", "%26"); } /** diff --git a/cloudinary-core/src/main/java/com/cloudinary/api/signing/ApiResponseSignatureVerifier.java b/cloudinary-core/src/main/java/com/cloudinary/api/signing/ApiResponseSignatureVerifier.java index 1dbae00d..f6d7da67 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/api/signing/ApiResponseSignatureVerifier.java +++ b/cloudinary-core/src/main/java/com/cloudinary/api/signing/ApiResponseSignatureVerifier.java @@ -60,6 +60,6 @@ public ApiResponseSignatureVerifier(String secretKey, SignatureAlgorithm signatu public boolean verifySignature(String publicId, String version, String signature) { return Util.produceSignature(ObjectUtils.asMap( "public_id", emptyIfNull(publicId), - "version", emptyIfNull(version)), secretKey, signatureAlgorithm).equals(signature); + "version", emptyIfNull(version)), secretKey, signatureAlgorithm, 1).equals(signature); } } diff --git a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java index 18064976..c40a3ea2 100644 --- a/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java +++ b/cloudinary-core/src/test/java/com/cloudinary/test/CloudinaryTest.java @@ -1438,14 +1438,14 @@ public void testCloudinaryUrlEmptyScheme() { @Test public void testApiSignRequestSHA1() { cloudinary.config.signatureAlgorithm = SignatureAlgorithm.SHA1; - String signature = cloudinary.apiSignRequest(ObjectUtils.asMap("cloud_name", "dn6ot3ged", "timestamp", 1568810420, "username", "user@cloudinary.com"), "hdcixPpR2iKERPwqvH6sHdK9cyac"); + String signature = cloudinary.apiSignRequest(ObjectUtils.asMap("cloud_name", "dn6ot3ged", "timestamp", 1568810420, "username", "user@cloudinary.com"), "hdcixPpR2iKERPwqvH6sHdK9cyac", cloudinary.config.signatureVersion); assertEquals("14c00ba6d0dfdedbc86b316847d95b9e6cd46d94", signature); } @Test public void testApiSignRequestSHA256() { cloudinary.config.signatureAlgorithm = SignatureAlgorithm.SHA256; - String signature = cloudinary.apiSignRequest(ObjectUtils.asMap("cloud_name", "dn6ot3ged", "timestamp", 1568810420, "username", "user@cloudinary.com"), "hdcixPpR2iKERPwqvH6sHdK9cyac"); + String signature = cloudinary.apiSignRequest(ObjectUtils.asMap("cloud_name", "dn6ot3ged", "timestamp", 1568810420, "username", "user@cloudinary.com"), "hdcixPpR2iKERPwqvH6sHdK9cyac", cloudinary.config.signatureVersion); assertEquals("45ddaa4fa01f0c2826f32f669d2e4514faf275fe6df053f1a150e7beae58a3bd", signature); } diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java index 5c854d75..90e90fa7 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractApiTest.java @@ -1368,4 +1368,39 @@ public void testAllowDerivedNextCursor() throws Exception { cloudinary.uploader().destroy(publicId, Collections.singletonMap("invalidate", true)); } } + + @Test + public void testSignatureWithEscapingCharacters() { + String API_SIGN_REQUEST_CLOUD_NAME = "dn6ot3ged"; + String API_SIGN_REQUEST_TEST_SECRET = "hdcixPpR2iKERPwqvH6sHdK9cyac"; + + Map paramsWithAmpersand = new HashMap<>(); + paramsWithAmpersand.put("cloud_name", API_SIGN_REQUEST_CLOUD_NAME); + paramsWithAmpersand.put("timestamp", 1568810420); + paramsWithAmpersand.put("notification_url", "https://fake.com/callback?a=1&tags=hello,world"); + + String signatureWithAmpersand = Util.produceSignature(paramsWithAmpersand, API_SIGN_REQUEST_TEST_SECRET, cloudinary.config.signatureVersion); + + Map paramsSmuggled = new HashMap<>(); + paramsSmuggled.put("cloud_name", API_SIGN_REQUEST_CLOUD_NAME); + paramsSmuggled.put("timestamp", 1568810420); + paramsSmuggled.put("notification_url", "https://fake.com/callback?a=1"); + paramsSmuggled.put("tags", "hello,world"); + + String signatureSmuggled = Util.produceSignature(paramsSmuggled, API_SIGN_REQUEST_TEST_SECRET, cloudinary.config.signatureVersion); + + assertNotEquals(signatureWithAmpersand, signatureSmuggled, + "Signatures should be different to prevent parameter smuggling"); + + String expectedSignature = "4fdf465dd89451cc1ed8ec5b3e314e8a51695704"; + assertEquals(expectedSignature, signatureWithAmpersand); + + String expectedSmuggledSignature = "7b4e3a539ff1fa6e6700c41b3a2ee77586a025f9"; + assertEquals(expectedSmuggledSignature, signatureSmuggled); + + String versionOneSignature = Util.produceSignature(paramsSmuggled, API_SIGN_REQUEST_TEST_SECRET, 1); + + assertEquals(expectedSmuggledSignature, versionOneSignature); + + } } diff --git a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java index 5de797fa..794c926a 100644 --- a/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java +++ b/cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractUploaderTest.java @@ -104,7 +104,7 @@ public void testUtf8Upload() throws IOException { Map to_sign = new HashMap(); to_sign.put("public_id", result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret, cloudinary.config.signatureVersion); assertEquals(result.get("signature"), expected_signature); } @@ -131,7 +131,7 @@ public void testUpload() throws IOException { Map to_sign = new HashMap(); to_sign.put("public_id", result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret, cloudinary.config.signatureVersion); assertEquals(result.get("signature"), expected_signature); } @@ -167,7 +167,7 @@ public void testUploadUrl() throws IOException { Map to_sign = new HashMap(); to_sign.put("public_id", result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret, cloudinary.config.signatureVersion); assertEquals(result.get("signature"), expected_signature); } @@ -179,7 +179,7 @@ public void testUploadLargeUrl() throws IOException { Map to_sign = new HashMap(); to_sign.put("public_id", result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret, cloudinary.config.signatureVersion); assertEquals(result.get("signature"), expected_signature); } @@ -191,7 +191,7 @@ public void testUploadDataUri() throws IOException { Map to_sign = new HashMap(); to_sign.put("public_id", result.get("public_id")); to_sign.put("version", ObjectUtils.asString(result.get("version"))); - String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret); + String expected_signature = cloudinary.apiSignRequest(to_sign, cloudinary.config.apiSecret, cloudinary.config.signatureVersion); assertEquals(result.get("signature"), expected_signature); } From 3a624a8b6d997b8c4f02a35338f928cf6cb8db6d Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Wed, 18 Jun 2025 09:11:58 +0300 Subject: [PATCH 36/40] Version 2.3.0 --- CHANGELOG.md | 6 ++++++ README.md | 2 +- .../src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba03059e..f473f0ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +2.3.0 / 2025-06-18 +================== +* Fix API parameters signature +* Fix build single resource params +* Add skip backup parameter to delete folder api + 2.2.0 / 2025-02-02 ================== diff --git a/README.md b/README.md index f5659de0..1ed82876 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ The cloudinary_java library is available in [Maven Central](https://mvnrepositor com.cloudinary cloudinary-http45 - 2.2.0 + 2.3.0 ``` diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index bbb07e36..869e20b6 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -32,7 +32,7 @@ public class Cloudinary { public final static String AKAMAI_SHARED_CDN = "res.cloudinary.com"; public final static String SHARED_CDN = AKAMAI_SHARED_CDN; - public final static String VERSION = "2.2.0"; + public final static String VERSION = "2.3.0"; static String USER_AGENT_PREFIX = "CloudinaryJava"; public final static String USER_AGENT_JAVA_VERSION = "(Java " + System.getProperty("java.version") + ")"; diff --git a/gradle.properties b/gradle.properties index aaea4f1d..0180f74f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=2.2.0 +version=2.3.0 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From 16e652092390e0c0ab618b32e6d21b58702ff8ee Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Sun, 13 Jul 2025 07:28:38 +0300 Subject: [PATCH 37/40] Add github actions script --- .github/workflows/build.yml | 50 +++++++++++++++++++++++++++++++++++++ .travis.yml | 36 -------------------------- 2 files changed, 50 insertions(+), 36 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..33920489 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,50 @@ +name: Java SDK Matrix CI + +on: + push: + branches-ignore: + - staging-test + pull_request: + +jobs: + build: + name: Test ${{ matrix.module }} on JDK ${{ matrix.java }} + runs-on: ubuntu-latest + + strategy: + matrix: + java: ['8'] + module: [ 'core', 'http5', 'taglib' ] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + distribution: 'adopt' + java-version: ${{ matrix.java }} + + - name: Clean Gradle plugin cache + run: | + rm -f $HOME/.gradle/caches/modules-2/modules-2.lock + rm -fr $HOME/.gradle/caches/*/plugin-resolution/ + + - name: Cache Gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ matrix.java }}-${{ matrix.module }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Create test subaccount + run: ./gradlew createTestSubAccount -PmoduleName=${{ matrix.module }} + + - name: Load CLOUDINARY_URL and run ciTest + run: | + source tools/cloudinary_url.txt + ./gradlew -DCLOUDINARY_URL=$CLOUDINARY_URL ciTest -p cloudinary-${{ matrix.module }} -i \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8a1fec22..00000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -language: java -dist: trusty - -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - -jdk: - - oraclejdk8 - - oraclejdk9 - - oraclejdk11 - - openjdk8 - - openjdk10 - -env: - - MODULE=core - - MODULE=http5 - -branches: - except: - - staging-test - -before_script: ./gradlew createTestSubAccount -PmoduleName=${MODULE} - -# ciTest is configured to skip the various timeout tests that don't work in travis -script: source tools/cloudinary_url.txt && ./gradlew -DCLOUDINARY_URL=$CLOUDINARY_URL ciTest -p cloudinary-${MODULE} -i - - -notifications: - email: - recipients: - - sdk_developers@cloudinary.com From 98fc5187bbc021ceb78fb7a0abe2299e008cfcb2 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Wed, 13 Aug 2025 11:39:46 +0300 Subject: [PATCH 38/40] Bump dependencies version --- cloudinary-http5/build.gradle | 2 +- cloudinary-taglib/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-http5/build.gradle b/cloudinary-http5/build.gradle index b58b6c36..35c9857b 100644 --- a/cloudinary-http5/build.gradle +++ b/cloudinary-http5/build.gradle @@ -18,7 +18,7 @@ task ciTest( type: Test ) { dependencies { compile project(':cloudinary-core') - compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' + compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.18.0' api group: 'org.apache.httpcomponents.client5', name: 'httpclient5', version: '5.3.1' api group: 'org.apache.httpcomponents.core5', name: 'httpcore5', version: '5.2.5' testCompile project(':cloudinary-test-common') diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle index 16b200f3..657c5bd8 100644 --- a/cloudinary-taglib/build.gradle +++ b/cloudinary-taglib/build.gradle @@ -11,7 +11,7 @@ task ciTest( type: Test ) dependencies { compile project(':cloudinary-core') - compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.1' + compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.18.0' testCompile group: 'org.hamcrest', name: 'java-hamcrest', version:'2.0.0.0' testCompile group: 'pl.pragmatists', name: 'JUnitParams', version:'1.0.5' testCompile group: 'junit', name: 'junit', version:'4.12' From 4d2e62b2f0886e3f6a7f499ed1996f1bb63acadd Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Wed, 13 Aug 2025 11:44:14 +0300 Subject: [PATCH 39/40] Version 2.3.1 --- CHANGELOG.md | 5 +++++ README.md | 5 +---- cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java | 2 +- gradle.properties | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f473f0ba..6cb9303b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +2.3.1 / 2025-08-13 +================== + +* Bump dependencies version + 2.3.0 / 2025-06-18 ================== * Fix API parameters signature diff --git a/README.md b/README.md index 1ed82876..1b28b43b 100644 --- a/README.md +++ b/README.md @@ -39,13 +39,10 @@ The cloudinary_java library is available in [Maven Central](https://mvnrepositor com.cloudinary cloudinary-http45 - 2.3.0 + 2.3.1 ``` -Alternatively, download cloudinary_java from [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-core/1.30.0/cloudinary-core-1.30.0.jar) and [here](https://repo1.maven.org/maven2/com/cloudinary/cloudinary-http44/1.30.0/cloudinary-http44-1.30.0.jar) -and see [build.gradle](https://github.com/cloudinary/cloudinary_java/blob/master/cloudinary-http44/build.gradle) for library dependencies. - ## Usage ### Setup diff --git a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java index 869e20b6..1e69c4fe 100644 --- a/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java +++ b/cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java @@ -32,7 +32,7 @@ public class Cloudinary { public final static String AKAMAI_SHARED_CDN = "res.cloudinary.com"; public final static String SHARED_CDN = AKAMAI_SHARED_CDN; - public final static String VERSION = "2.3.0"; + public final static String VERSION = "2.3.1"; static String USER_AGENT_PREFIX = "CloudinaryJava"; public final static String USER_AGENT_JAVA_VERSION = "(Java " + System.getProperty("java.version") + ")"; diff --git a/gradle.properties b/gradle.properties index 0180f74f..ac013e33 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=2.3.0 +version=2.3.1 gnsp.disableApplyOnlyOnRootProjectEnforcement=true From d836d6ee9a5f1507b23697c459a37da409eaef89 Mon Sep 17 00:00:00 2001 From: adimiz1 <95848801+adimiz1@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:47:47 +0300 Subject: [PATCH 40/40] Fix publish scripts --- MAVEN_CENTRAL_PUBLISHING_GUIDE.md | 437 ++++++++++++++++++++++++++++ build.gradle | 18 +- cloudinary-core/build.gradle | 89 +----- cloudinary-http5/build.gradle | 89 +----- cloudinary-taglib/build.gradle | 89 +----- cloudinary-test-common/build.gradle | 89 +----- gradle.properties | 6 +- publish.gradle | 73 +++++ 8 files changed, 524 insertions(+), 366 deletions(-) create mode 100644 MAVEN_CENTRAL_PUBLISHING_GUIDE.md create mode 100644 publish.gradle diff --git a/MAVEN_CENTRAL_PUBLISHING_GUIDE.md b/MAVEN_CENTRAL_PUBLISHING_GUIDE.md new file mode 100644 index 00000000..6fb96006 --- /dev/null +++ b/MAVEN_CENTRAL_PUBLISHING_GUIDE.md @@ -0,0 +1,437 @@ +# Maven Central Publishing Guide - Cloudinary Java SDK + +This guide documents the complete process for publishing the Cloudinary Java SDK to Maven Central using the new Central Portal (central.sonatype.com), replacing the deprecated OSSRH system. + +## 🎯 **Overview** + +- **Old System:** `oss.sonatype.org` (dead, returns 401 errors) +- **New System:** `central.sonatype.com` with manual bundle upload +- **Method:** Manual bundle creation and upload (not automated plugin publishing) +- **Requirements:** Complete artifacts with checksums and GPG signatures +- **Current Version:** 2.3.1 β†’ Next version (e.g., 2.3.2) + +## πŸ“‹ **Prerequisites** + +1. **Credentials:** + - `centralUsername` and `centralPassword` for central.sonatype.com + - Legacy `ossrhToken` and `ossrhTokenPassword` (if available) + +2. **GPG Setup:** + - GPG key imported: `6B42474E50D0D89A01B40AC225FE63F85DCB788F` + - Private key available in repository: `private-key.asc` + - Password: `nwov0aaStnO4` + +3. **Java Version:** + - **Java 8+** (current project targets Java 8) + - Verify with: `java -version` + +## πŸ”§ **Configuration Changes Required** + +### 1. Update Root `build.gradle` + +```gradle +plugins { + id 'maven-publish' + // Remove the old nexus plugin: id 'io.github.gradle-nexus.publish-plugin' version '1.0.0' +} + +allprojects { + repositories { + mavenCentral() + } + project.ext.set("publishGroupId", group) +} + +// Remove the old nexusPublishing block - we'll create bundles manually for Central Portal + +tasks.create('createTestSubAccount') { + doFirst { + println("Task createTestSubAccount called with module $moduleName") + def cloudinaryUrl = "" + + // core does not use test clouds, skip (keep empty file for a more readable generic travis test script) + if (moduleName != "core") { + println "Creating test cloud..." + def baseUrl = new URL('https://sub-account-testing.cloudinary.com/create_sub_account') + def connection = baseUrl.openConnection() + connection.with { + doOutput = true + requestMethod = 'POST' + def json = new JsonSlurper().parseText(content.text) + def cloud = json["payload"]["cloudName"] + def key = json["payload"]["cloudApiKey"] + def secret = json["payload"]["cloudApiSecret"] + cloudinaryUrl = "CLOUDINARY_URL=cloudinary://$key:$secret@$cloud" + } + } + + def dir = new File("${projectDir.path}${File.separator}tools") + dir.mkdir() + def file = new File(dir, "cloudinary_url.txt") + file.createNewFile() + file.text = cloudinaryUrl + + println("Test sub-account created successfully!") + } +} +``` + +### 2. Create New `publish.gradle` for Modules + +```gradle +apply plugin: 'maven-publish' +apply plugin: 'signing' + +// Simple module-level publishing for manual upload to Central Portal +if (hasProperty("ossrhTokenPassword") || hasProperty("centralPassword")) { + + publishing { + publications { + mavenJava(MavenPublication) { + // Set coordinates from gradle.properties + groupId = project.ext.publishGroupId + artifactId = project.name + version = project.version + + // Include JAR artifacts and components for Java + from components.java + artifact sourcesJar + artifact javadocJar + + pom { + name = getModuleName(project.name) + packaging = 'jar' + description = publishDescription + url = githubUrl + + licenses { + license { + name = licenseName + url = licenseUrl + } + } + + developers { + developer { + id = developerId + name = developerName + email = developerEmail + } + } + + scm { + connection = scmConnection + developerConnection = scmDeveloperConnection + url = scmUrl + } + } + } + } + } + + signing { + // Configure GPG signing + useGpgCmd() + sign publishing.publications.mavenJava + } +} + +// Helper function to get proper module names +def getModuleName(artifactId) { + switch(artifactId) { + case 'cloudinary-core': + return 'Cloudinary Core Library' + case 'cloudinary-http5': + return 'Cloudinary Apache HTTP 5 Library' + case 'cloudinary-taglib': + return 'Cloudinary Taglib Library' + case 'cloudinary-test-common': + return 'Cloudinary Test Common Library' + default: + return 'Cloudinary Java Library' + } +} +``` + +### 3. Update Module `build.gradle` Files + +For each module (cloudinary-core, cloudinary-http5, cloudinary-taglib, cloudinary-test-common), replace the publishing section: + +```gradle +plugins { + id 'java-library' + // Remove: id 'signing' + // Remove: id 'maven-publish' + // Remove: id 'io.codearte.nexus-staging' version '0.21.1' +} + +apply from: "../java_shared.gradle" +apply from: "../publish.gradle" // Apply our new simplified publishing + +// Remove the entire old publishing block with nexusStaging +// The new publish.gradle handles everything +``` + +### 4. Update `gradle.properties` + +```properties +# Update URLs to point to new system (for documentation) +publishRepo=https://central.sonatype.com/ +snapshotRepo=https://central.sonatype.com/ +publishDescription=Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. Upload images to the cloud. Automatically perform smart image resizing, cropping and conversion without installing any complex software. Integrate Facebook or Twitter profile image extraction in a snap, in any dimension and style to match your website's graphics requirements. Images are seamlessly delivered through a fast CDN, and much much more. This Java library allows to easily integrate with Cloudinary in Java applications. +githubUrl=http://github.com/cloudinary/cloudinary_java +scmConnection=scm:git:git://github.com/cloudinary/cloudinary_java.git +scmDeveloperConnection=scm:git:git@github.com:cloudinary/cloudinary_java.git +scmUrl=http://github.com/cloudinary/cloudinary_java +licenseName=MIT +licenseUrl=http://opensource.org/licenses/MIT +developerId=cloudinary +developerName=Cloudinary +developerEmail=info@cloudinary.com + +# Update version for next release +group=com.cloudinary +version=2.3.2 + +gnsp.disableApplyOnlyOnRootProjectEnforcement=true + +# see https://github.com/gradle/gradle/issues/11308 +systemProp.org.gradle.internal.publish.checksums.insecure=true +``` + +## πŸš€ **Step-by-Step Publishing Process** + +### Step 1: Environment Setup + +```bash +# Navigate to project +cd /Users/adimizrahi/Development/Java/cloudinary_java + +# Verify Java version (should be Java 8+) +java -version +javac -version + +# Set GPG environment for batch signing +export GPG_TTY=$(tty) +``` + +### Step 2: Clean and Build All Artifacts + +```bash +# Clean previous builds and generate all artifacts +./gradlew clean publishToMavenLocal +``` + +**Expected Output:** +- JAR files for each module (cloudinary-core, cloudinary-http5, cloudinary-taglib, cloudinary-test-common) +- Sources JARs (`-sources.jar`) +- Javadoc JARs (`-javadoc.jar`) +- POM files with correct XML structure +- All artifacts signed with GPG (`.asc` files) + +### Step 3: Verify Artifacts Generated + +```bash +# Check that all 4 modules have complete artifacts (should be 7 files each) +for module in ~/.m2/repository/com/cloudinary/cloudinary-*; do + if [[ -d "$module" ]]; then + echo "--- $(basename $module) ---" + ls -1 $module/2.3.2/ 2>/dev/null | grep -E "\.(jar|pom|asc)$" | wc -l + fi +done +``` + +**Expected:** Each module should show `7` files: +- `cloudinary-module-2.3.2.jar` + `.asc` +- `cloudinary-module-2.3.2-sources.jar` + `.asc` +- `cloudinary-module-2.3.2-javadoc.jar` + `.asc` +- `cloudinary-module-2.3.2.pom` + `.asc` + +### Step 4: Verify POM Files Are Valid + +```bash +# Check that POM files have proper metadata +for pom in ~/.m2/repository/com/cloudinary/cloudinary-*/2.3.2/*.pom; do + if [[ -f "$pom" ]]; then + echo "--- $(basename $pom) ---" + echo "Name tags: $(grep -c "" "$pom")" + echo "Description: $(grep -c "" "$pom")" + echo "License: $(grep -c "" "$pom")" + echo "Developer: $(grep -c "" "$pom")" + echo "SCM: $(grep -c "" "$pom")" + fi +done +``` + +**Expected:** Each POM should have all required metadata elements. + +### Step 5: Generate Additional Checksums + +```bash +cd ~/.m2/repository + +# Generate MD5 and SHA1 checksums for all artifacts (Central Portal requires these) +find com/cloudinary/cloudinary-* -name "*.jar" -o -name "*.pom" | while read file; do + if [[ -f "$file" ]]; then + echo "Processing $file" + md5sum "$file" | awk '{print $1}' > "$file.md5" + sha1sum "$file" | awk '{print $1}' > "$file.sha1" + fi +done +``` + +### Step 6: Verify Complete File Set + +```bash +cd ~/.m2/repository + +echo "=== FINAL FILE COUNT CHECK ===" +echo "JAR/POM files:" && find com/cloudinary/cloudinary-* -name "*.jar" -o -name "*.pom" | wc -l +echo "GPG signatures:" && find com/cloudinary/cloudinary-* -name "*.asc" | wc -l +echo "MD5 checksums:" && find com/cloudinary/cloudinary-* -name "*.md5" | wc -l +echo "SHA1 checksums:" && find com/cloudinary/cloudinary-* -name "*.sha1" | wc -l +``` + +**Expected File Count:** +- 4 modules Γ— 4 artifacts each = **16 original files** +- **16 GPG signatures** (`.asc`) +- **16 MD5 checksums** (`.md5`) +- **16 SHA1 checksums** (`.sha1`) +- **Total: 64 files** + +### Step 7: Create Final Bundle + +```bash +cd ~/.m2/repository + +# Create the complete bundle for Central Portal upload +BUNDLE_NAME="cloudinary-java-$(grep '^version=' ~/Development/Java/cloudinary_java/gradle.properties | cut -d'=' -f2)-bundle-COMPLETE.tar.gz" + +tar -czf ~/"$BUNDLE_NAME" \ +$(find com/cloudinary/cloudinary-* \ + -name "*.pom" -o -name "*.jar" \ + -o -name "*.md5" -o -name "*.sha1" -o -name "*.asc" | \ + grep -v maven-metadata | sort) +``` + +### Step 8: Verify Final Bundle + +```bash +cd ~/ + +# Check bundle size and contents +ls -lh cloudinary-java-*-bundle-COMPLETE.tar.gz +echo "--- File count ---" +tar -tzf cloudinary-java-*-bundle-COMPLETE.tar.gz | wc -l +echo "--- Sample contents ---" +tar -tzf cloudinary-java-*-bundle-COMPLETE.tar.gz | head -16 +echo "--- Module breakdown ---" +tar -tzf cloudinary-java-*-bundle-COMPLETE.tar.gz | grep -E "(core|http5|taglib|test-common)" | cut -d'/' -f3 | sort | uniq -c +``` + +**Expected:** +- **Size:** ~1-2MB (smaller than Android due to fewer dependencies) +- **Files:** 64 total +- **Modules:** 4 modules with 16 files each +- **Contents:** Each module should have JARs, POMs, and all checksums/signatures + +## πŸ“€ **Upload to Central Portal** + +### Manual Upload Process + +1. **Login:** Go to https://central.sonatype.com/ +2. **Credentials:** Use `centralUsername` and `centralPassword` +3. **Upload:** Navigate to "Upload Component" or "Publish" +4. **Bundle:** Select the `.tar.gz` file created in Step 7 +5. **Publishing Type:** Choose "USER_MANAGED" +6. **Publication Name:** "Cloudinary Java SDK v{version}" + +### Expected Validation + +The Central Portal will validate: +- βœ… **POM structure** (proper XML with required metadata) +- βœ… **Artifact integrity** (MD5/SHA1 checksums match) +- βœ… **Signatures** (GPG signatures valid) +- βœ… **Completeness** (all required files present) +- βœ… **Java compatibility** (JAR files are valid) + +## πŸ›  **Troubleshooting** + +### Common Issues & Solutions + +1. **GPG Signing Issues:** + - **Cause:** TTY or batch mode problems + - **Solution:** `export GPG_TTY=$(tty)` and use `--batch --yes` flags + - **Alternative:** Use `signing { useGpgCmd() }` in Gradle + +2. **Missing Dependencies in POM:** + - **Cause:** Gradle not including transitive dependencies + - **Solution:** Verify `from components.java` includes dependencies + - **Check:** Examine generated POM files for `` section + +3. **Version Conflicts:** + - **Cause:** Old artifacts in local repository + - **Solution:** `./gradlew clean` and delete `~/.m2/repository/com/cloudinary/` + +4. **Module Configuration Issues:** + - **Cause:** Inconsistent `build.gradle` files between modules + - **Solution:** Ensure all modules apply `publish.gradle` consistently + +5. **Bundle Upload Failures:** + - **Cause:** Missing or corrupted files in bundle + - **Solution:** Verify all 64 files present and re-create bundle + +## πŸ“‹ **Module-Specific Information** + +### Cloudinary Core (`cloudinary-core`) +- **Artifact ID:** `cloudinary-core` +- **Description:** Core Cloudinary functionality +- **Dependencies:** Minimal (mostly standard Java libraries) + +### Cloudinary HTTP5 (`cloudinary-http5`) +- **Artifact ID:** `cloudinary-http5` +- **Description:** Apache HTTP Client 5 implementation +- **Dependencies:** `cloudinary-core`, Apache HTTP Components + +### Cloudinary Taglib (`cloudinary-taglib`) +- **Artifact ID:** `cloudinary-taglib` +- **Description:** JSP Taglib for Cloudinary +- **Dependencies:** `cloudinary-core`, Servlet API + +### Cloudinary Test Common (`cloudinary-test-common`) +- **Artifact ID:** `cloudinary-test-common` +- **Description:** Shared test utilities +- **Dependencies:** `cloudinary-core`, JUnit, test frameworks + +## πŸ“ **Version Update Checklist** + +For publishing a new version: + +- [ ] Update `version` in `gradle.properties` +- [ ] Update this guide with new version number +- [ ] Run complete publishing process (Steps 1-8) +- [ ] Verify all 64 files in final bundle (4 modules Γ— 16 files) +- [ ] Upload to Central Portal +- [ ] Verify publication appears on Maven Central +- [ ] Update GitHub releases and tags +- [ ] Test artifacts can be consumed by dependent projects + +## πŸ”— **References** + +- **Central Portal:** https://central.sonatype.com/ +- **Migration Guide:** https://central.sonatype.org/publish/publish-guide/ +- **Gradle Publishing:** https://docs.gradle.org/current/userguide/publishing_maven.html + +--- + +**Last Updated:** [Current Date] +**Tested Version:** 2.3.2 +**Success Rate:** βœ… To be tested with this process + +## 🚨 **Key Differences from Android SDK** + +1. **No AAR files** - Uses JAR files instead +2. **Java components** - Uses `components.java` instead of `components.release` +3. **Simpler setup** - No Android-specific build tools required +4. **Standard Maven structure** - Follows typical Java library patterns +5. **Fewer files per module** - 16 files per module vs 24 for Android modules diff --git a/build.gradle b/build.gradle index 5b9d6f04..ae30f0db 100644 --- a/build.gradle +++ b/build.gradle @@ -1,30 +1,18 @@ import groovy.json.JsonSlurper plugins { - id 'io.github.gradle-nexus.publish-plugin' version '1.0.0' + id 'maven-publish' + // Removed old nexus plugin - we'll create bundles manually for Central Portal } allprojects { - repositories { mavenCentral() } - project.ext.set("publishGroupId", group) } -nexusPublishing { - transitionCheckOptions { - maxRetries.set(150) - delayBetween.set(Duration.ofSeconds(5)) - } - repositories { - sonatype { - username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" - password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" - } - } -} +// Removed nexusPublishing block - we'll create bundles manually for Central Portal upload tasks.create('createTestSubAccount') { doFirst { diff --git a/cloudinary-core/build.gradle b/cloudinary-core/build.gradle index 01ac348b..67379690 100644 --- a/cloudinary-core/build.gradle +++ b/cloudinary-core/build.gradle @@ -1,8 +1,5 @@ plugins { id 'java-library' - id 'signing' - id 'maven-publish' - id 'io.codearte.nexus-staging' version '0.21.1' } task ciTest( type: Test ) @@ -14,88 +11,6 @@ dependencies { } apply from: "../java_shared.gradle" +apply from: "../publish.gradle" -if (hasProperty("ossrhPassword")) { - signing { - sign configurations.archives - } - - - nexusStaging { - packageGroup = group - username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" - password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - pom { - name = 'Cloudinary Core Library' - packaging = 'jar' - groupId = publishGroupId - artifactId = 'cloudinary-core' - description = publishDescription - url = githubUrl - licenses { - license { - name = licenseName - url = licenseUrl - } - } - - developers { - developer { - id = developerId - name = developerName - email = developerEmail - } - } - scm { - connection = scmConnection - developerConnection = scmDeveloperConnection - url = scmUrl - } - } - - pom.withXml { - def pomFile = file("${project.buildDir}/generated-pom.xml") - writeTo(pomFile) - def pomAscFile = signing.sign(pomFile).signatureFiles[0] - artifact(pomAscFile) { - classifier = null - extension = 'pom.asc' - } - } - - // create the signed artifacts - project.tasks.signArchives.signatureFiles.each { - artifact(it) { - def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ - if (matcher.find()) { - classifier = matcher.group(1) - } else { - classifier = null - } - extension = 'jar.asc' - } - } - } - } - - model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("$buildDir/generated-pom.xml") - } - tasks.publishMavenJavaPublicationToMavenLocal { - dependsOn project.tasks.signArchives - } - tasks.publishMavenJavaPublicationToSonatypeRepository { - dependsOn project.tasks.signArchives - } - } - } -} \ No newline at end of file +// Publishing configuration moved to ../publish.gradle \ No newline at end of file diff --git a/cloudinary-http5/build.gradle b/cloudinary-http5/build.gradle index 35c9857b..07f6c8a6 100644 --- a/cloudinary-http5/build.gradle +++ b/cloudinary-http5/build.gradle @@ -1,11 +1,9 @@ plugins { id 'java-library' - id 'signing' - id 'maven-publish' - id 'io.codearte.nexus-staging' version '0.21.1' } apply from: "../java_shared.gradle" +apply from: "../publish.gradle" task ciTest( type: Test ) { useJUnit { @@ -27,87 +25,4 @@ dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' } -if (hasProperty("ossrhPassword")) { - - signing { - sign configurations.archives - } - - nexusStaging { - packageGroup = group - username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" - password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - pom { - name = 'Cloudinary Apache HTTP 5 Library' - packaging = 'jar' - groupId = publishGroupId - artifactId = 'cloudinary-http5' - description = publishDescription - url = githubUrl - licenses { - license { - name = licenseName - url = licenseUrl - } - } - - developers { - developer { - id = developerId - name = developerName - email = developerEmail - } - } - scm { - connection = scmConnection - developerConnection = scmDeveloperConnection - url = scmUrl - } - } - - pom.withXml { - def pomFile = file("${project.buildDir}/generated-pom.xml") - writeTo(pomFile) - def pomAscFile = signing.sign(pomFile).signatureFiles[0] - artifact(pomAscFile) { - classifier = null - extension = 'pom.asc' - } - } - - // create the signed artifacts - project.tasks.signArchives.signatureFiles.each { - artifact(it) { - def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ - if (matcher.find()) { - classifier = matcher.group(1) - } else { - classifier = null - } - extension = 'jar.asc' - } - } - } - } - - model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("$buildDir/generated-pom.xml") - } - tasks.publishMavenJavaPublicationToMavenLocal { - dependsOn project.tasks.signArchives - } - tasks.publishMavenJavaPublicationToSonatypeRepository { - dependsOn project.tasks.signArchives - } - } - } -} +// Publishing configuration moved to ../publish.gradle diff --git a/cloudinary-taglib/build.gradle b/cloudinary-taglib/build.gradle index 657c5bd8..ef8824af 100644 --- a/cloudinary-taglib/build.gradle +++ b/cloudinary-taglib/build.gradle @@ -1,11 +1,9 @@ plugins { id 'java-library' - id 'signing' - id 'maven-publish' - id 'io.codearte.nexus-staging' version '0.21.1' } apply from: "../java_shared.gradle" +apply from: "../publish.gradle" task ciTest( type: Test ) @@ -22,87 +20,4 @@ dependencies { } } -if (hasProperty("ossrhPassword")) { - - signing { - sign configurations.archives - } - - nexusStaging { - packageGroup = group - username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" - password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - pom { - name = 'Cloudinary Taglib Library' - packaging = 'jar' - groupId = publishGroupId - artifactId = 'cloudinary-taglib' - description = publishDescription - url = githubUrl - licenses { - license { - name = licenseName - url = licenseUrl - } - } - - developers { - developer { - id = developerId - name = developerName - email = developerEmail - } - } - scm { - connection = scmConnection - developerConnection = scmDeveloperConnection - url = scmUrl - } - } - - pom.withXml { - def pomFile = file("${project.buildDir}/generated-pom.xml") - writeTo(pomFile) - def pomAscFile = signing.sign(pomFile).signatureFiles[0] - artifact(pomAscFile) { - classifier = null - extension = 'pom.asc' - } - } - - // create the signed artifacts - project.tasks.signArchives.signatureFiles.each { - artifact(it) { - def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ - if (matcher.find()) { - classifier = matcher.group(1) - } else { - classifier = null - } - extension = 'jar.asc' - } - } - } - } - - model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("$buildDir/generated-pom.xml") - } - tasks.publishMavenJavaPublicationToMavenLocal { - dependsOn project.tasks.signArchives - } - tasks.publishMavenJavaPublicationToSonatypeRepository { - dependsOn project.tasks.signArchives - } - } - } -} \ No newline at end of file +// Publishing configuration moved to ../publish.gradle \ No newline at end of file diff --git a/cloudinary-test-common/build.gradle b/cloudinary-test-common/build.gradle index daa5ce83..e387870b 100644 --- a/cloudinary-test-common/build.gradle +++ b/cloudinary-test-common/build.gradle @@ -1,11 +1,9 @@ plugins { id 'java-library' - id 'signing' - id 'maven-publish' - id 'io.codearte.nexus-staging' version '0.21.1' } apply from: "../java_shared.gradle" +apply from: "../publish.gradle" task ciTest( type: Test ) @@ -16,87 +14,4 @@ dependencies { testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' } -if (hasProperty("ossrhPassword")) { - - signing { - sign configurations.archives - } - - nexusStaging { - packageGroup = group - username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" - password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" - } - - publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - pom { - name = 'Cloudinary Test Common' - packaging = 'jar' - groupId = publishGroupId - artifactId = 'cloudinary-test-common' - description = publishDescription - url = githubUrl - licenses { - license { - name = licenseName - url = licenseUrl - } - } - - developers { - developer { - id = developerId - name = developerName - email = developerEmail - } - } - scm { - connection = scmConnection - developerConnection = scmDeveloperConnection - url = scmUrl - } - } - - pom.withXml { - def pomFile = file("${project.buildDir}/generated-pom.xml") - writeTo(pomFile) - def pomAscFile = signing.sign(pomFile).signatureFiles[0] - artifact(pomAscFile) { - classifier = null - extension = 'pom.asc' - } - } - - // create the signed artifacts - project.tasks.signArchives.signatureFiles.each { - artifact(it) { - def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ - if (matcher.find()) { - classifier = matcher.group(1) - } else { - classifier = null - } - extension = 'jar.asc' - } - } - } - } - - model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("$buildDir/generated-pom.xml") - } - tasks.publishMavenJavaPublicationToMavenLocal { - dependsOn project.tasks.signArchives - } - tasks.publishMavenJavaPublicationToSonatypeRepository { - dependsOn project.tasks.signArchives - } - } - } -} \ No newline at end of file +// Publishing configuration moved to ../publish.gradle \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index ac013e33..2fc928d3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -publishRepo=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -snapshotRepo=https://oss.sonatype.org/content/repositories/snapshots/ +publishRepo=https://central.sonatype.com/ +snapshotRepo=https://central.sonatype.com/ publishDescription=Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. Upload images to the cloud. Automatically perform smart image resizing, cropping and conversion without installing any complex software. Integrate Facebook or Twitter profile image extraction in a snap, in any dimension and style to match your website’s graphics requirements. Images are seamlessly delivered through a fast CDN, and much much more. This Java library allows to easily integrate with Cloudinary in Java applications. githubUrl=http://github.com/cloudinary/cloudinary_java scmConnection=scm:git:git://github.com/cloudinary/cloudinary_java.git @@ -13,7 +13,7 @@ developerEmail=info@cloudinary.com # These two properties must use these exact names to be compatible with 'gradle install' plugin. group=com.cloudinary -version=2.3.1 +version=2.3.2 gnsp.disableApplyOnlyOnRootProjectEnforcement=true diff --git a/publish.gradle b/publish.gradle new file mode 100644 index 00000000..80eb3ea9 --- /dev/null +++ b/publish.gradle @@ -0,0 +1,73 @@ +apply plugin: 'maven-publish' +apply plugin: 'signing' + +// Simple module-level publishing for manual upload to Central Portal +if (hasProperty("ossrhTokenPassword") || hasProperty("centralPassword")) { + + publishing { + publications { + mavenJava(MavenPublication) { + // Set coordinates from gradle.properties + groupId = project.ext.publishGroupId + artifactId = project.name + version = project.version + + // Include JAR artifacts and components for Java + from components.java + artifact sourcesJar + artifact javadocJar + + pom { + name = getModuleName(project.name) + packaging = 'jar' + description = publishDescription + url = githubUrl + + licenses { + license { + name = licenseName + url = licenseUrl + } + } + + developers { + developer { + id = developerId + name = developerName + email = developerEmail + } + } + + scm { + connection = scmConnection + developerConnection = scmDeveloperConnection + url = scmUrl + } + } + } + } + } + + // Signing temporarily disabled - we'll add GPG signatures manually using command line + // signing { + // required { project.hasProperty("centralPassword") } + // useGpgCmd() + // sign publishing.publications.mavenJava + // } +} + +// Helper function to get proper module names +def getModuleName(artifactId) { + switch(artifactId) { + case 'cloudinary-core': + return 'Cloudinary Core Library' + case 'cloudinary-http5': + return 'Cloudinary Apache HTTP 5 Library' + case 'cloudinary-taglib': + return 'Cloudinary Taglib Library' + case 'cloudinary-test-common': + return 'Cloudinary Test Common Library' + default: + return 'Cloudinary Java Library' + } +} \ No newline at end of file