From 43d8e9ac0c7f073017d9c4b566c9566dea95f205 Mon Sep 17 00:00:00 2001 From: adimiz1 Date: Sun, 29 Sep 2024 15:39:43 +0300 Subject: [PATCH 01/18] 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 02/18] 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 03/18] 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 04/18] 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 05/18] 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 06/18] 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 07/18] 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 08/18] 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 09/18] 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 10/18] 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 11/18] 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 12/18] 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 13/18] 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 14/18] 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 15/18] 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 16/18] 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 17/18] 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 18/18] 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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy